so://phie

figure

The vanilla alternative for writing JSX-based React applications
git clone git://soophie.de/figure

blob: ac31b9837c3f6a2c0f54be5bb886b95df81bd3fc (2944 bytes)[doc][blame][raw]

1const parser = new DOMParser();

2let counter = 1000;

3

4export const createTlx = (createElement) => {

5 function tlx(parts, ...props) {

6 const [html, refs] = ltr(parts, props);

7 const dom = parser.parseFromString(html, 'text/html');

8 if(dom.body.childNodes.length !== 1) {

9 throw 'invalid DOM structure!';

10 }

11 const node = dom.body.childNodes[0];

12 const elements = render(node, refs);

13 if(elements.length !== 1) {

14 throw 'invalid VDOM structure!';

15 }

16 return elements[0];

17 }

18

19 function render(node, refs) {

20 if(node.nodeType === Node.TEXT_NODE) {

21 return apply(node.textContent, refs);

22 }

23 const tag = node.tagName.toLowerCase();

24 const attributes = {};

25 for(const attribute of node.attributes) {

26 const key = attribute.name;

27 const value = attribute.textContent;

28 let isDynamic = false;

29 for(const ref in refs) {

30 if(value === ref) {

31 let match = null;

32 if((match = /^@(\w+)$/.exec(key)) !== null) {

33 const event = match[1];

34 attributes[`on${event.substr(0, 1).toUpperCase()}${event.substr(1)}`] = refs[ref];

35 }

36 else {

37 attributes[key] = refs[ref];

38 }

39 isDynamic = true;

40 break;

41 }

42 }

43 if(!isDynamic) {

44 if(key === 'style') {

45 const styles = {};

46 for(const item of value.split(';')) {

47 if(item.trim().length === 0) {

48 break;

49 }

50 let [key, value] = item.split(':');

51 key = key.trim();

52 let match = null;

53 if((match = /-(\w)/.exec(key)) !== null) {

54 const char = match[1];

55 key = key.replace(`-${char}`, char.toUpperCase());

56 }

57 value = value.trim();

58 styles[key] = apply(value, refs).join('');

59 }

60 attributes[key] = styles;

61 }

62 else {

63 attributes[key] = apply(value, refs).join('');

64 }

65 }

66 }

67 const children = [];

68 (node.childNodes ?? []).forEach((child) => children.push(...render(child, refs)));

69 return [createElement(tag, attributes, ...children)];

70 }

71

72 function apply(value, refs) {

73 let values = [];

74 const expr = /\$tlx-\d+/g;

75 let match = null;

76 let last = 0;

77 while((match = expr.exec(value)) !== null) {

78 const index = match.index;

79 values.push(value.substring(last, index));

80 values.push(refs[match[0]]);

81 last = index + match[0].length;

82 }

83 values.push(value.substring(last));

84 return values;

85 }

86

87 function ltr(parts, props) {

88 let string = '';

89 const refs = {};

90 for(let i = 0; i < parts.length; i++) {

91 string += parts[i];

92 if(props[i] !== undefined) {

93 const id = `$tlx-${counter}`;

94 refs[id] = props[i];

95 string += id ?? '';

96 counter++;

97 }

98 }

99 return [string.trim(), refs];

100 }

101

102 return tlx;

103}