Page Hotreloading
This commit is contained in:
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -7,3 +7,12 @@
|
||||
[submodule "incbin"]
|
||||
path = incbin
|
||||
url = https://github.com/graphitemaster/incbin.git
|
||||
[submodule "build-id"]
|
||||
path = build-id
|
||||
url = https://github.com/mattst88/build-id.git
|
||||
[submodule "cJSON"]
|
||||
path = cJSON
|
||||
url = https://github.com/DaveGamble/cJSON.git
|
||||
[submodule "md5-c"]
|
||||
path = md5-c
|
||||
url = https://github.com/Zunawe/md5-c.git
|
||||
|
2
baked.c
2
baked.c
@ -13,6 +13,7 @@ INCBIN(page_missing_t, "./tmpls/page-missing.t");
|
||||
|
||||
INCBIN(simple_min_css, "./etc/simple.min.css");
|
||||
INCBIN(favicon_ico, "./etc/favicon.ico");
|
||||
INCBIN(hotreload_js, "./etc/hotreload.js");
|
||||
|
||||
Baked_Resource *baked_resources = NULL;
|
||||
|
||||
@ -34,6 +35,7 @@ void init_baked_resources(void)
|
||||
add_baked_resource("gpp1", gpp1_data, gpp1_size);
|
||||
add_baked_resource("simple.min.css", simple_min_css_data, simple_min_css_size);
|
||||
add_baked_resource("favicon.ico", favicon_ico_data, favicon_ico_size);
|
||||
add_baked_resource("hotreload.js", hotreload_js_data, hotreload_js_size);
|
||||
}
|
||||
|
||||
void free_baked_resources(void)
|
||||
|
1
baked.h
1
baked.h
@ -10,6 +10,7 @@ INCBIN_EXTERN(page_missing_t);
|
||||
|
||||
INCBIN_EXTERN(simple_min_css);
|
||||
INCBIN_EXTERN(favicon_ico);
|
||||
INCBIN_EXTERN(hotreload_js);
|
||||
|
||||
typedef struct {
|
||||
char *key; // path
|
||||
|
11
build.c
11
build.c
@ -20,7 +20,8 @@ int main(int argc, char ** argv)
|
||||
"./mongoose.o",
|
||||
"./gpp1",
|
||||
"./tmpls/home.t",
|
||||
"./tmpls/page-missing.t"
|
||||
"./tmpls/page-missing.t",
|
||||
"./etc/hotreload.js"
|
||||
) {
|
||||
|
||||
RULE("./mongoose.o", "./mongoose/mongoose.c") {
|
||||
@ -36,13 +37,15 @@ int main(int argc, char ** argv)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
CMD("cc", "-fPIC", "-ggdb", "-I.", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX",
|
||||
CMD("cc", "-fPIC", "-ggdb", "-I.", "-DDEBUG=1", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX",
|
||||
"-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", "-o", "./aboba",
|
||||
"./main.c", "./routes.c", "./baked.c", "./mongoose.o");
|
||||
"./main.c", "./routes.c", "./baked.c", "./mongoose.o", "./cJSON/cJSON.c",
|
||||
"./md5-c/md5.c");
|
||||
#else
|
||||
CMD("cc", "-fPIC", "-I.", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX",
|
||||
"-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", "-o", "./aboba",
|
||||
"./main.c", "./routes.c", "./baked.c", "./mongoose.o");
|
||||
"./main.c", "./routes.c", "./baked.c", "./mongoose.o", "./cJSON/cJSON.c",
|
||||
"./md5-c/md5.c");
|
||||
#endif
|
||||
}
|
||||
} else if (strcmp(cmd, "clean") == 0) {
|
||||
|
1
cJSON
Submodule
1
cJSON
Submodule
Submodule cJSON added at 8f2beb57dd
47
etc/hotreload.js
Normal file
47
etc/hotreload.js
Normal file
@ -0,0 +1,47 @@
|
||||
let build_id = "";
|
||||
|
||||
async function fetch_build_id()
|
||||
{
|
||||
const response = await fetch("/build-id");
|
||||
const json = await response.json();
|
||||
return json.build_id;
|
||||
}
|
||||
|
||||
(async function() {
|
||||
build_id = await fetch_build_id();
|
||||
})();
|
||||
|
||||
|
||||
const asyncIntervals = [];
|
||||
|
||||
const runAsyncInterval = async (cb, interval, intervalIndex) => {
|
||||
await cb();
|
||||
if (asyncIntervals[intervalIndex]) {
|
||||
setTimeout(() => runAsyncInterval(cb, interval, intervalIndex), interval);
|
||||
}
|
||||
};
|
||||
|
||||
const setAsyncInterval = (cb, interval) => {
|
||||
if (cb && typeof cb === "function") {
|
||||
const intervalIndex = asyncIntervals.length;
|
||||
asyncIntervals.push(true);
|
||||
runAsyncInterval(cb, interval, intervalIndex);
|
||||
return intervalIndex;
|
||||
} else {
|
||||
throw new Error('Callback must be a function');
|
||||
}
|
||||
};
|
||||
|
||||
const clearAsyncInterval = (intervalIndex) => {
|
||||
if (asyncIntervals[intervalIndex]) {
|
||||
asyncIntervals[intervalIndex] = false;
|
||||
}
|
||||
};
|
||||
|
||||
setAsyncInterval(async function () {
|
||||
let new_build_id = await fetch_build_id();
|
||||
if (build_id !== new_build_id) {
|
||||
location.reload(true);
|
||||
}
|
||||
}, 1000);
|
||||
|
6
main.c
6
main.c
@ -2,9 +2,7 @@
|
||||
|
||||
#define GEBS_IMPLEMENTATION
|
||||
#include "gebs/gebs.h"
|
||||
|
||||
#include "mongoose/mongoose.h"
|
||||
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb/stb_ds.h"
|
||||
|
||||
@ -32,13 +30,15 @@ void event_handler(struct mg_connection *conn, int ev, void *ev_data)
|
||||
}
|
||||
}
|
||||
|
||||
static void init_route_hashtable(void)
|
||||
void init_route_hashtable(void)
|
||||
{
|
||||
shdefault(route_hashtable, &route_page_not_found);
|
||||
shput(route_hashtable, "/page-missing", &route_page_not_found);
|
||||
shput(route_hashtable, "/simple.min.css", &route_simple_css);
|
||||
shput(route_hashtable, "/favicon.ico", &route_favicon);
|
||||
shput(route_hashtable, "/hotreload.js", &route_hotreload_js);
|
||||
shput(route_hashtable, "/", &route_home);
|
||||
shput(route_hashtable, "/build-id", &route_build_id);
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
|
1
md5-c
Submodule
1
md5-c
Submodule
Submodule md5-c added at f3529b666b
58
routes.c
58
routes.c
@ -1,5 +1,7 @@
|
||||
#include "gebs/gebs.h"
|
||||
#include "mongoose/mongoose.h"
|
||||
#include "cJSON/cJSON.h"
|
||||
#include "md5-c/md5.h"
|
||||
|
||||
#include "routes.h"
|
||||
#include "baked.h"
|
||||
@ -29,6 +31,31 @@ bool gpp_run(char *path, NString_List *env, String_Builder *out)
|
||||
return cmd_run_collect(&cmd, out) == 0;
|
||||
}
|
||||
|
||||
void route_build_id(struct mg_connection *conn, struct mg_http_message *msg)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
defer { cJSON_Delete(root); }
|
||||
|
||||
char *time = __TIME__;
|
||||
uchar md5_buf[16];
|
||||
md5String(time, md5_buf);
|
||||
String_Builder sb = {0};
|
||||
defer { sb_free(&sb); }
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
char tmp[3];
|
||||
snprintf(tmp, sizeof(tmp), "%02x", md5_buf[i]);
|
||||
sb_append_nstr(&sb, tmp);
|
||||
}
|
||||
sb_finish(&sb);
|
||||
|
||||
cJSON_AddItemToObject(root, "build_id", cJSON_CreateString(sb.items));
|
||||
|
||||
char *root_text = cJSON_PrintUnformatted(root);
|
||||
defer { free(root_text); }
|
||||
|
||||
mg_http_reply(conn, 200, "Content-Type: application/json\r\n", root_text);
|
||||
}
|
||||
|
||||
void route_page_not_found(struct mg_connection *conn, struct mg_http_message *msg)
|
||||
{
|
||||
NString_List env = {0};
|
||||
@ -45,6 +72,12 @@ void route_page_not_found(struct mg_connection *conn, struct mg_http_message *ms
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
list_append(&env, "-DHOTRELOAD=1");
|
||||
#else
|
||||
list_append(&env, "-DHOTRELOAD=0");
|
||||
#endif
|
||||
|
||||
bool ok = gpp_run(path, &env, &out);
|
||||
sb_finish(&out);
|
||||
|
||||
@ -93,6 +126,25 @@ void route_favicon(struct mg_connection *conn, struct mg_http_message *msg)
|
||||
mg_http_reply(conn, 200, "Content-Type: image/x-icon\r\n", "%s", sb.items);
|
||||
}
|
||||
|
||||
void route_hotreload_js(struct mg_connection *conn, struct mg_http_message *msg)
|
||||
{
|
||||
char path[PATH_MAX] = {0};
|
||||
if (!get_baked_resource_path("hotreload.js", path, sizeof(path))) {
|
||||
mg_http_reply(conn, 500, "Content-Type: text/plain\r\n", INTERNAL_SERVER_ERROR_MSG);
|
||||
return;
|
||||
}
|
||||
|
||||
String_Builder sb = {0};
|
||||
defer { sb_free(&sb); }
|
||||
if (!sb_read_file(&sb, path)) {
|
||||
mg_http_reply(conn, 500, "Content-Type: text/plain\r\n", INTERNAL_SERVER_ERROR_MSG);
|
||||
return;
|
||||
}
|
||||
sb_finish(&sb);
|
||||
|
||||
mg_http_reply(conn, 200, "Content-Type: text/javascript\r\n", "%s", sb.items);
|
||||
}
|
||||
|
||||
void route_home(struct mg_connection *conn, struct mg_http_message *msg)
|
||||
{
|
||||
NString_List env = {0};
|
||||
@ -107,6 +159,12 @@ void route_home(struct mg_connection *conn, struct mg_http_message *msg)
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
list_append(&env, "-DHOTRELOAD=1");
|
||||
#else
|
||||
list_append(&env, "-DHOTRELOAD=0");
|
||||
#endif
|
||||
|
||||
bool ok = gpp_run(path, &env, &out);
|
||||
sb_finish(&out);
|
||||
|
||||
|
2
routes.h
2
routes.h
@ -6,6 +6,8 @@
|
||||
void route_page_not_found(struct mg_connection *conn, struct mg_http_message *msg);
|
||||
void route_simple_css(struct mg_connection *conn, struct mg_http_message *msg);
|
||||
void route_favicon(struct mg_connection *conn, struct mg_http_message *msg);
|
||||
void route_hotreload_js(struct mg_connection *conn, struct mg_http_message *msg);
|
||||
void route_home(struct mg_connection *conn, struct mg_http_message *msg);
|
||||
void route_build_id(struct mg_connection *conn, struct mg_http_message *msg);
|
||||
|
||||
#endif // ROUTES_H_
|
||||
|
@ -41,5 +41,9 @@
|
||||
<footer>
|
||||
<a href="/">HOME</a>
|
||||
</footer>
|
||||
|
||||
<#if HOTRELOAD>
|
||||
<script src="/hotreload.js"></script>
|
||||
<#endif>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -12,5 +12,9 @@
|
||||
<footer>
|
||||
<a href="/">HOME</a>
|
||||
</footer>
|
||||
|
||||
<#if HOTRELOAD>
|
||||
<script src="/hotreload.js"></script>
|
||||
<#endif>
|
||||
</body>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user