@cilix/lightjs 0.0.4 → 0.0.6

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 (3) hide show
  1. package/core.js +139 -53
  2. package/index.js +1 -1
  3. package/package.json +1 -1
package/core.js CHANGED
@@ -19,11 +19,9 @@
19
19
  * USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  */
21
21
 
22
- const fs = require('fs');
23
- const path = require("path");
24
-
25
- module.exports = (config) => {
22
+ module.exports = (config, fs, path) => {
26
23
  const _files = {};
24
+ let includeRuntime = false;
27
25
 
28
26
  function throw_error (err) {
29
27
  err.origin = 'light';
@@ -351,7 +349,9 @@ module.exports = (config) => {
351
349
  "let",
352
350
  "nosync",
353
351
  "as",
354
- "global"
352
+ "global",
353
+ "when",
354
+ "catch"
355
355
  ];
356
356
 
357
357
  let symbols = [
@@ -661,10 +661,15 @@ module.exports = (config) => {
661
661
  const parse = (tokens) => {
662
662
  let tok = tokens[0];
663
663
  let cursor = 0;
664
- let in_tag = false;
665
664
  let ast = { type: 'file', data: { file: tok.file }, children: [] };
666
665
  let parent = ast;
667
666
 
667
+ const parse_state = {
668
+ in_tag: false,
669
+ if_depth: 0,
670
+ each_depth: 0
671
+ };
672
+
668
673
  function ast_node(type, data) {
669
674
  let node = { type, data, children: [] };
670
675
  parent.children.push(node);
@@ -1014,6 +1019,7 @@ module.exports = (config) => {
1014
1019
  attr[key] = { type: "bool", data: true };
1015
1020
  }
1016
1021
  } else if (accept("on")) {
1022
+ includeRuntime = true;
1017
1023
  expect(":");
1018
1024
  let handler;
1019
1025
  let evt = tok.data;
@@ -1030,9 +1036,9 @@ module.exports = (config) => {
1030
1036
  }
1031
1037
 
1032
1038
  function parse_custom_tag_body () {
1033
- in_tag = true;
1039
+ parse_state.in_tag = true;
1034
1040
  parse_tag_list();
1035
- in_tag = false;
1041
+ parse_state.in_tag = false;
1036
1042
  }
1037
1043
 
1038
1044
  function parse_tag() {
@@ -1084,6 +1090,7 @@ module.exports = (config) => {
1084
1090
  }
1085
1091
 
1086
1092
  function parse_if_statement() {
1093
+ parse_state.if_depth++;
1087
1094
  expect("(");
1088
1095
  let condition = parse_expr();
1089
1096
  expect(")");
@@ -1112,13 +1119,68 @@ module.exports = (config) => {
1112
1119
  }
1113
1120
  }
1114
1121
  parent = current;
1122
+ parse_state.if_depth--;
1115
1123
  }
1116
1124
 
1117
- const parse_tag_list = () => {
1125
+ function parse_when_statement() {
1126
+ if (parse_state.each_depth > 0) {
1127
+ throw_error({
1128
+ msg: 'when cannot be nested in each',
1129
+ pos: tok.pos,
1130
+ file: tok.file
1131
+ });
1132
+ }
1133
+ if (parse_state.if_depth > 0) {
1134
+ throw_error({
1135
+ msg: 'when cannot be nested in if',
1136
+ pos: tok.pos,
1137
+ file: tok.file
1138
+ });
1139
+ }
1140
+ expect("(");
1141
+ let condition = parse_expr();
1142
+ expect(")");
1143
+ let current = parent;
1144
+ let node = ast_node('when', condition);
1145
+ parent = node;
1146
+ let pass = ast_node('block');
1147
+ parent = pass;
1148
+ if (accept("[")) {
1149
+ parse_tag_list();
1150
+ expect("]");
1151
+ } else {
1152
+ parse_tag();
1153
+ }
1154
+ expect("else");
1155
+ parent = node;
1156
+ let fail = ast_node('block');
1157
+ parent = fail;
1158
+ if (accept("[")) {
1159
+ parse_tag_list();
1160
+ expect("]");
1161
+ } else {
1162
+ parse_tag();
1163
+ }
1164
+ parent = current;
1165
+ expect("catch");
1166
+ parent = node;
1167
+ let catchBlock = ast_node('block');
1168
+ parent = catchBlock;
1169
+ if (accept("[")) {
1170
+ parse_tag_list();
1171
+ expect("]");
1172
+ } else {
1173
+ parse_tag();
1174
+ }
1175
+ parent = current;
1176
+ }
1177
+
1178
+ function parse_tag_list() {
1118
1179
  if (accept("if")) {
1119
1180
  parse_if_statement();
1120
1181
  parse_tag_list();
1121
1182
  } else if (accept("each")) {
1183
+ parse_state.each_depth++;
1122
1184
  expect("(");
1123
1185
  let it1, it0 = tok.data;
1124
1186
  expect("ident");
@@ -1142,6 +1204,10 @@ module.exports = (config) => {
1142
1204
  parse_tag();
1143
1205
  }
1144
1206
  parent = current;
1207
+ parse_state.each_depth--;
1208
+ parse_tag_list();
1209
+ } else if (accept("when")) {
1210
+ parse_when_statement();
1145
1211
  parse_tag_list();
1146
1212
  } else if (peek("ident")) {
1147
1213
  parse_tag();
@@ -1154,10 +1220,11 @@ module.exports = (config) => {
1154
1220
  ast_node('yield', { pos: tok.pos, file: tok.file });
1155
1221
  next();
1156
1222
  parse_tag_list();
1157
- } else if (in_tag && (peek('global') || peek('const') || peek('let'))) {
1223
+ } else if (parse_state.in_tag && (peek('global') || peek('const') || peek('let'))) {
1158
1224
  parse_assignment();
1159
1225
  parse_tag_list();
1160
- } else if (in_tag && peek('js_context')) {
1226
+ } else if (parse_state.in_tag && peek('js_context')) {
1227
+ includeRuntime = true;
1161
1228
  parent.js = tok.data;
1162
1229
  ast_node('js', { js: tok.data, pos: tok.pos, file: tok.file });
1163
1230
  next();
@@ -1296,6 +1363,7 @@ module.exports = (config) => {
1296
1363
  } else if (peek('const') || peek('let') || peek('global')) {
1297
1364
  parse_assignment();
1298
1365
  } else if (peek('js_context')) {
1366
+ includeRuntime = true;
1299
1367
  ast_node('js', { js: tok.data, pos: tok.pos, file: tok.file });
1300
1368
  js_found = true;
1301
1369
  next();
@@ -1320,7 +1388,7 @@ module.exports = (config) => {
1320
1388
  let inScript = false;
1321
1389
  let inStyle = false;
1322
1390
 
1323
- const found = { head: false, head: false, body: false };
1391
+ const found = { head: false, html: false, body: false };
1324
1392
 
1325
1393
  const emit = flush ? (txt) => {
1326
1394
  if (html.length > 100) {
@@ -1432,7 +1500,6 @@ module.exports = (config) => {
1432
1500
  ctx.pop();
1433
1501
  break;
1434
1502
  }
1435
- // todo: html -> head, body enforcement
1436
1503
  if (valid_tags.indexOf(node.data.name) === -1) {
1437
1504
  err(`Invalid tag: ${node.data.name}`, node.data);
1438
1505
  }
@@ -1447,9 +1514,11 @@ module.exports = (config) => {
1447
1514
  }
1448
1515
  emit('<');
1449
1516
  emit(node.data.name);
1517
+ let innerText = null;
1450
1518
  for (let a in attr) {
1451
1519
  let val = evaluate(attr[a]);
1452
1520
  const t = getType(val);
1521
+ if (a === 'innerHTML') innerText = val;
1453
1522
  if (val !== false || a === 'innerHTML') {
1454
1523
  emit(' ');
1455
1524
  emit(a.replace('bind:', ''));
@@ -1473,11 +1542,13 @@ module.exports = (config) => {
1473
1542
  emit('>');
1474
1543
  if (void_tags.indexOf(node.data.name) === -1) {
1475
1544
  if (node.data.name === 'html') {
1545
+ if (innerText) err('cannot set innerHTML of html tag');
1476
1546
  if (found.html) err('html tag may only be used once', node.data);
1477
1547
  found.html = true;
1478
1548
  } else if (!found.html) {
1479
1549
  err('html tag must be the first tag printed', node.data);
1480
1550
  } else if (node.data.name === 'head') {
1551
+ if (innerText) err('cannot set innerHTML of head tag');
1481
1552
  if (found.head) err('head tag may only be used once', node.data);
1482
1553
  found.head = true;
1483
1554
  } else if (node.data.name === 'body') {
@@ -1485,23 +1556,27 @@ module.exports = (config) => {
1485
1556
  if (found.body) err('body tag may only be used once', node.data);
1486
1557
  found.body = true;
1487
1558
  }
1488
- node.children.forEach(walk);
1489
- if (node.data.name === 'head') {
1490
- if (js) {
1491
- emit('<script>(function (data) {');
1492
- emit(js);
1493
- emit('})(');
1494
- emit(JSON.stringify(initial_state));
1495
- emit(');<' + '/' + 'script>');
1496
- } else {
1497
- const rt = `(function (data){${js}})(${JSON.stringify(initial_state)})`;
1498
- emit('<script>' + rt + '<' + '/script>');
1559
+ if (innerText) {
1560
+ emit(innerText);
1561
+ emit('</');
1562
+ emit(node.data.name);
1563
+ emit('>');
1564
+ } else {
1565
+ node.children.forEach(walk);
1566
+ if (node.data.name === 'head') {
1567
+ if (includeRuntime) {
1568
+ emit('<script>');
1569
+ emit(`document.addEventListener('DOMContentLoaded', function () {\n`);
1570
+ emit(`(function (data){${js}})(${JSON.stringify(initial_state)})`);
1571
+ emit('});');
1572
+ emit('<' + '/script>');
1573
+ }
1499
1574
  }
1575
+ emit('</');
1576
+ emit(node.data.name);
1577
+ emit('>');
1578
+ inScript = false;
1500
1579
  }
1501
- emit('</');
1502
- emit(node.data.name);
1503
- emit('>');
1504
- inScript = false;
1505
1580
  }
1506
1581
  } break;
1507
1582
  case 'if': {
@@ -1520,14 +1595,14 @@ module.exports = (config) => {
1520
1595
  if (t === 'array' || t === 'string') {
1521
1596
  for (let i = 0; i < val.length; i++) {
1522
1597
  s[it[0]] = val[i];
1523
- s[it[1]] = i;
1598
+ if (it[1]) s[it[1]] = i;
1524
1599
  node.children.forEach(walk);
1525
1600
  }
1526
1601
  } else if (t === 'object') {
1527
1602
  const keys = Object.keys(val);
1528
1603
  for (let i = 0; i < keys.length; i++) {
1529
1604
  s[it[0]] = keys[i];
1530
- s[it[1]] = val[keys[i]];
1605
+ if (it[1]) s[it[1]] = val[keys[i]];
1531
1606
  node.children.forEach(walk);
1532
1607
  }
1533
1608
  } else {
@@ -1804,7 +1879,7 @@ module.exports = (config) => {
1804
1879
  if (t === 'object' || t === 'null' || t === 'array') {
1805
1880
  stack.push(JSON.stringify(val));
1806
1881
  } else {
1807
- stack.push(e.toString());
1882
+ stack.push(val.toString());
1808
1883
  }
1809
1884
  } break;
1810
1885
  case 'join': {
@@ -2183,7 +2258,7 @@ module.exports = (config) => {
2183
2258
  out[out.length - 1].code += txt;
2184
2259
  if (fileIdx > -1) {
2185
2260
  const n = fileList[fileIdx].name;
2186
- out[out.length - 1].parent_dir = getPathInfo(n).parent;
2261
+ out[out.length - 1].parent_dir = path ? getPathInfo(n).parent : '/';
2187
2262
  }
2188
2263
  };
2189
2264
 
@@ -2631,7 +2706,8 @@ module.exports = (config) => {
2631
2706
 
2632
2707
  createFileList(ast);
2633
2708
 
2634
- emit(`document.addEventListener('DOMContentLoaded', function () {\n`);
2709
+ // emit(`document.addEventListener('DOMContentLoaded', function () {\n`);
2710
+ emit('(function () {')
2635
2711
  emit(`${light_runtime}`);
2636
2712
  emit('var $sync = function () {};\n');
2637
2713
  for (let g in globals) {
@@ -2675,33 +2751,35 @@ module.exports = (config) => {
2675
2751
  });
2676
2752
 
2677
2753
  emit('$sync();\n');
2678
- emit('});\n');
2754
+ // emit('});\n');
2755
+ emit('})();\n');
2679
2756
 
2680
2757
  return out;
2681
2758
  };
2682
2759
 
2683
- const renderToAst = async (file, actions) => {
2684
- const fileText = openFile(file);
2760
+ const renderToAst = async (file, actions, _fileText) => {
2761
+ const fileText = file === 'default' ? _fileText : openFile(file);
2685
2762
  const tokens = tokenize(fileText, file);
2686
2763
  const ast = parse(tokens);
2687
- const runtime = generateRuntime(ast, actions);
2688
- let js;
2689
-
2690
- if (config.jsTransform) {
2691
- js = (await Promise.all(runtime.map(async (chunk) => {
2692
- if (chunk.transform) {
2693
- return await config.jsTransform(chunk);
2694
- }
2695
- return chunk.code;
2696
- }))).join('\n');
2697
- } else {
2698
- js = runtime.map((chunk) => chunk.code).join('\n');
2699
- }
2764
+ let js = '';
2765
+ if (includeRuntime) {
2766
+ const runtime = generateRuntime(ast, actions);
2767
+
2768
+ if (config.jsTransform) {
2769
+ js = (await Promise.all(runtime.map(async (chunk) => {
2770
+ if (chunk.transform) {
2771
+ return await config.jsTransform(chunk);
2772
+ }
2773
+ return chunk.code;
2774
+ }))).join('\n');
2775
+ } else {
2776
+ js = runtime.map((chunk) => chunk.code).join('\n');
2777
+ }
2700
2778
 
2701
- if (config.jsPostProcess) {
2702
- js = await config.jsPostProcess(js);
2779
+ if (config.jsPostProcess) {
2780
+ js = await config.jsPostProcess(js);
2781
+ }
2703
2782
  }
2704
-
2705
2783
  return { ast, js };
2706
2784
  };
2707
2785
 
@@ -2720,11 +2798,19 @@ module.exports = (config) => {
2720
2798
  return html;
2721
2799
  };
2722
2800
 
2801
+ const renderString = async (fileText, data) => {
2802
+ _files.default = fileText;
2803
+ const result = await renderToAst('default', [], fileText);
2804
+ const html = renderToHTML(result, data);
2805
+
2806
+ return html;
2807
+ };
2808
+
2723
2809
  const renderToCache = async (file, actions) => {
2724
2810
  const result = await renderToAst(file, actions);
2725
2811
  return JSON.stringify(result);
2726
2812
  };
2727
2813
 
2728
- return { render, renderToCache, renderToHTML, printError };
2814
+ return { render, renderString, renderToCache, renderToHTML, printError };
2729
2815
  };
2730
2816
 
package/index.js CHANGED
@@ -108,7 +108,7 @@ Light.compile = async (name, opts) => {
108
108
  });
109
109
  return result.outputFiles[0].text;
110
110
  }
111
- });
111
+ }, fs, path);
112
112
  try {
113
113
  let html;
114
114
  if (opts.cache) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cilix/lightjs",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "A new kind of JavaScript framework",
5
5
  "main": "index.js",
6
6
  "scripts": {