Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/danbugs/dancing_web

This is a proof-of-concept of a modern C web-framework that compiles to Wasm and is used for building user interfaces.
https://github.com/danbugs/dancing_web

c web-framework webassembly

Last synced: about 1 month ago
JSON representation

This is a proof-of-concept of a modern C web-framework that compiles to Wasm and is used for building user interfaces.

Awesome Lists containing this project

README

        

# DanCing Web 💃🕸 (DCW)

## Getting Started

Dancing Web is now distributed with the [Tarantella Package Manager](https://github.com/danbugs/tarantella) — a tool I've made to simplify setup of projects like these!

To install the Tarantella Package Manager, you need the Rust toolchain — for instructions on how to install it, see [this](https://www.rust-lang.org/tools/install). Once you're done with that, you can install Tarantella by running: `cargo install tarantella`.

To start-off, setup a new main module WASM project with: `tapm new my-first-project`. This will start a new C WASM project with:
- a build folder,
- a dependencies folder,
- an index.html,
- a `Makefile`,
- a releases folder,
- a src folder with some starting code in `main.c`, and a
- `Tarantella.toml` file.

Next, add the latest version of Dancing Web as a dependency with: `tapm add "danbugs/dancing_web"`. This will automatically download DCW to your dependencies folder, add it to your `Tarantella.toml` list of dependencies, and append `dependencies/dcw/dcw.o` to the `DEPENDENCIES` variable of your `Makefile` to facilitate compilation.

Now, let's create our first HTML-equivalent "C Markdown Language" file (`.cml`) to be rendered from C! First, create a folder called `frontend/` and, inside it, create a `hello_world.cml` file with the following code:

```HTML
HTML(


${hello_world()}$


.red {
color: red;
}

);
```

You can call C functions within the `${` and `}$` markers (called renderable markers) or within the `$E` and `}$` markers (called executable markers).
- Renderable markers are for C functions that return content that is meant to be rendered, and
- Executable markers are for C functions that are meant to be called but don't necessarily render HTML (e.g., functions within a button's `onclick`).

Next, in our `main.c`, replace its' contents with:

```C
#include
#include
#include
#include "../dependencies/dcw/dcw.h"

extern void display_html(html_t raw_html);
// ^^^ rendering function from DCW that is
// external to our main module

// this is compulsory for functions
EMSCRIPTEN_KEEPALIVE // <- you intend to call from HTML
html_t hello_world()
{
// ^^^ functions that return content to be rendered
// must return the html_t or char* types

html_t tmp;
// ^^^ you don't need to free this malloc,
// the framework will do it for you.

asprintf(&tmp, "%s", "Hello, World!");
// ^^^ this works because html_t expands
// to char*

return tmp;
}
// ^^^ this is the hello_world function
// we are calling from our .cml file

int main()
{
html_t html =
#include "../frontend/hello_world.cml"
;
// ^^^ this brings the .cml component into scope!
// things to note:
// - the #include statement must be in a line of its'
// own, and
// - the ';' is optional but, if present, must also be
// on a line of its' own
// (usually, I like to include the ';' because it
// helps with formatting)

display_html(html);
// ^^^ this will render our html.
}
```

To finish off, in your `Makefile`, append `--js-library dependencies/dcw/dcw.js` to your `EMCC_FLAGS` variable. It should look like this: `EMCC_CFLAGS=-s MAIN_MODULE=1 --js-library dependencies/dcw/dcw.js`.

Now, let's build the project with: `tapm build`. This will use the `Makefile` to compile the project to the `build/` folder.

To test the project, run: `tapm run`. Navigating to `http://127.0.0.1:4000` in your browser, you should see:

![getting-started-1](https://i.imgur.com/zGWqSow.png)

For more complex examples, view the project in the `examples/` folder of the repo!

## Appendix

### Why use `.cml` files?

If you are using an IDE, to avoid C auto-formatting the HTML to something that doesn't work, you can create separate files for it. These files can have any extension but I've been using `.cml` (i.e., C Markup Language).

In addition, to get code auto-formatting working for `.cml` files on VSCode, open `settings.json` and add the following at the end of it:

```
"files.associations": {
"*.cml": "html"
},
```

### An alternative to `.cml` files

You can write HTML directly within C, like this:

```
html_t html = HTML(


${hello_world()}$


.red {
color: red;
}

);
```

...or:

```
html_t html = HTMLIFY(" \

\

${hello_world()}$

\
\
\
.red { \
color: red; \
} \
\
");
```

### Returning other types as `html_t`

```
EMSCRIPTEN_KEEPALIVE
html_t add_two_numbers(int a, int b)
{
char *tmp;
int result = a + b;
asprintf(&tmp, "%d", result);
return tmp;
}
```

### Returning unmalloc-ed items

For the record, a function like:

```
EMSCRIPTEN_KEEPALIVE
html_t hello_world()
{
return "Hello, World!";
}
```
... would also work. Thing is, it is not the best because the framework will try to free a pointer that wasn't malloc-ed. While this doesn't cause an error, I wouldn't call it a best practice.

### Notes to Self

To check for any problems (i.e., mem leaks and whatnot) in the code at runtime, compile like so: `emcc hello_world.c ../../src/dcw.c --js-library ../../src/dcw.js -fsanitize=address -s ALLOW_MEMORY_GROWTH -s INITIAL_MEMORY=285212672 -gsource-map --source-map-base http://127.0.0.1:4000`. After that, run with: `tapm run`.