1 module libwasm.spa; 2 3 version (LDC) import ldc.attributes; 4 public import libwasm.types; 5 public import libwasm.dom; 6 public import libwasm.node; 7 public import libwasm.event; 8 public import libwasm.array; 9 public import libwasm.css; 10 public import libwasm.router; 11 12 nothrow: 13 @safe void log_info(string msg) 14 { 15 import libwasm.bindings.Console; 16 17 console.info(msg); 18 } 19 20 @safe void log_error(string msg) 21 { 22 import libwasm.bindings.Console; 23 24 console.error(msg); 25 } 26 27 char[] parse_int(long num) nothrow @trusted 28 { 29 import libwasm.rt.allocator : ThreadMemAllocator; 30 31 // used only for debugging memutils (for now) 32 import fast.format; 33 34 char[] buf = cast(char[]) ThreadMemAllocator.allocate(decCharsVal(num)); 35 return formattedWrite!"%d"(buf.ptr, num); 36 } 37 38 extern (C) 39 { 40 Handle getRoot(); 41 } 42 43 version (unittest) 44 { 45 auto assumeNoThrow(T)(lazy T block) 46 { 47 try 48 { 49 return block(); 50 } 51 catch (Exception e) 52 { 53 assert(false, e.msg); 54 } 55 } 56 57 auto renderToNode(T)(auto ref T t) @trusted 58 { 59 import unit_threaded; 60 61 Handle rootIdx = cast(Handle) unittest_dom_nodes.data.length; 62 auto rootNode = new UnittestDomNode(NodeType.root, rootIdx + 1); 63 unittest_dom_nodes.put(rootNode); 64 t.compile(); 65 libwasm.dom.render(JsHandle(rootIdx + 1), t); 66 assert(t.getNamedNode().node != invalidHandle); 67 assert(t.getNamedNode().mounted == true); 68 return rootNode; 69 } 70 71 string renderToString(T)(auto ref T t) 72 { 73 import std.format : format; 74 75 static if (is(T : UnittestDomNode)) 76 { 77 auto node = t; 78 } 79 else 80 auto node = t.renderToNode; 81 return format("%s", node).assumeNoThrow; 82 } 83 84 string renderToString(T)() 85 { 86 T t; 87 return t.renderToString(); 88 } 89 } 90 91 void addApplicationCss(Application, Theme)() 92 { 93 enum css = GetCss!(Application, Theme); 94 static if (css.length > 0) 95 addCss(css); 96 } 97 98 mixin template Spa(Application) 99 { 100 struct Empty 101 { 102 } 103 104 mixin Spa!(Application, Empty); 105 } 106 107 mixin template Spa(Application, Theme) 108 { 109 __gshared Application application; 110 pragma(mangle, "_start") 111 extern (C) 112 export 113 @trusted void _start(uint heap_base) 114 { 115 import libwasm.rt.memory; 116 import fast.internal.helpers : logInfo, logError; 117 import memutils.constants : writeln, parseInt; 118 import memutils.scoped; 119 alloc_init(heap_base); 120 PoolStack.initialize(); 121 auto root = getRoot(); 122 addApplicationCss!(Application, Theme)(); 123 writeln = &log_info; 124 parseInt = &parse_int; 125 logInfo = &log_info; 126 logError = &log_error; 127 static if (__traits(hasMember, Application, "main")) 128 { 129 application.main(); 130 } 131 application.compile(); 132 setupRouter(); 133 application.registerRoutes(); 134 libwasm.dom.render(root, application); 135 static if (__traits(hasMember, Application, "ready")) 136 { 137 application.ready(); 138 } 139 else router().navigateTo(document().location().front.pathname()); 140 } 141 142 version (hmr) 143 { 144 pragma(msg, "libwasm with HMR"); 145 import libwasm.hmr; 146 147 pragma(mangle, "dumpApp") 148 extern (C) export string dumpApp() 149 { 150 return application.dumpState(); 151 } 152 153 pragma(mangle, "loadApp") 154 extern (C) export void loadApp(string state) 155 { 156 application.loadState(state); 157 } 158 } 159 } 160 161 struct Parameters(Ps...) 162 { 163 static template opDispatch(string name) 164 { 165 alias opDispatch(alias P) = Parameters!(Ps, Param!(name, P)); // does this use only public fields? 166 } 167 } 168 169 auto param() 170 { 171 return Parameters!()(); 172 } 173 174 struct Param(string name, alias field) 175 { 176 alias Name = name; 177 alias Field = field; 178 }