Skip to content

Commit

Permalink
website
Browse files Browse the repository at this point in the history
  • Loading branch information
mbasso committed Feb 6, 2018
1 parent ddf9810 commit acb364b
Show file tree
Hide file tree
Showing 39 changed files with 2,729 additions and 2 deletions.
18 changes: 17 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,24 @@ script:
branches:
only:
- master
- develop
cache:
directories:
- $HOME/.npm
after_success: npm run test:cov

jobs:
include:
- stage: Deploy website
language: node_js
node_js: "9"
install: true
script: skip
deploy:
provider: script
script:
- git config --global user.email "${GH_EMAIL}"
- git config --global user.name "${GH_NAME}"
- echo "machine github.com login ${GH_NAME} password ${GH_TOKEN}" > ~/.netrc
- cd website && npm install && GIT_USER="${GH_NAME}" npm run publish-gh-pages
on:
branch: master
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ int main() {

asm-dom aims to be used from C++, however it can be used also from javascript, here you can find the doc of both:

- [C++ docs](https://github.com/mbasso/asm-dom/blob/master/docs/cpp.md)
- [C++ docs](https://mbasso.github.io/asm-dom/docs/installation.html)
- [JS docs](https://github.com/mbasso/asm-dom/blob/master/docs/js.md)

## Ecosystem
Expand Down
18 changes: 18 additions & 0 deletions docs/boolean-attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
id: boolean-attributes
title: Boolean Attributes
---

If you want to set a boolean attribute, like `readonly`, you can just pass true or false as string, asm-dom will handle it for you:

```c++
VNode* vnode = h("input",
Data(
Attrs {
{"type", "text"}
{"readonly", "true"}
// or {"readonly", "false"}
},
)
);
```
8 changes: 8 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
id: examples
title: Examples
---

Examples are available in the [examples folder](https://github.com/mbasso/asm-dom/tree/master/examples):

- [TODO MVC](https://github.com/mbasso/asm-dom/tree/master/examples/todomvc%20-%20cpp)
64 changes: 64 additions & 0 deletions docs/h.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
id: h
title: h
---

You can create vnodes using `h` function. `h` accepts a tag/selector as a `std::string`, an optional `Data` struct and an optional `std::string` or a vector of children. Here is the list of signatures:

```c++
VNode* h(const std::string& sel);
VNode* h(const std::string& sel, const std::string& text);
VNode* h(const std::string& text, true); // used to create text nodes
VNode* h(const std::string& sel, const Data& data);
VNode* h(const std::string& sel, const Children& children);
VNode* h(const std::string& sel, VNode* child);
VNode* h(const std::string& sel, const Data& data, const std::string& text);
VNode* h(const std::string& sel, const Data& data, const Children& children);
VNode* h(const std::string& sel, const Data& data, VNode* child);
```
The data object contains optional attributes, optional props and optional callbacks. Also, attributes can contain 2 special keys:
- `ns`: the namespace URI to associate with the element
- `key`: this property is used to keep pointers to DOM nodes that existed previously to avoid recreating them if it is unnecessary. This is very useful for things like list reordering.
Here is an example, please use our `typedef` to do that:
```c++
// typedef std::function<bool(emscripten::val)> Callback;
// typedef std::unordered_map<std::string, std::string> Attrs;
// typedef std::unordered_map<std::string, emscripten::val> Props;
// typedef std::unordered_map<std::string, Callback> Callbacks;
// typedef std::vector<VNode*> Children;
VNode* vnode = h("div",
Data(
Attrs {{"style", "color: #000"}}
),
Children {
h("h1", string("Headline")),
h("p", string("A paragraph")),
}
);
VNode* vnode2 = h("div",
Data(
Attrs {
{"id", "an-id"}, // node.setAttribute('id', 'an-id')
{"key", "foo"},
{"class", "foo"}, // node.setAttribute('class', 'foo')
{"data-foo", "bar"} // a dataset attribute
},
Props {
{"foo", emscripten::val(7)} // node.foo = 7
},
Children {
{"onclick", [](emscripten::val e) -> bool {
// do stuff...
return true;
}}
}
)
);
```
22 changes: 22 additions & 0 deletions docs/init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
id: init
title: init
---

The `init` function has to been called before using `asm-dom`, in the `main` function, to prepare its environment. It takes a `Config` struct defined as follows:

```c++
struct Config {
bool clearMemory = true;
bool unsafePatch = false;
}
```
- `clearMemory`: `true` by default, set it to `false` if you want to free memory manually, for more information see [memory management](memory-management.html).
- `unsafePatch`: `false` by default, set it to `true` if you haven't a single `patch` in your application. This allows you to call patch with an `oldVnode` that hasn't been used previously.
```c++
Config config = Config();
config.unsafePatch = true;
init(config);
```
74 changes: 74 additions & 0 deletions docs/inline-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
id: inline-example
title: Inline example
---

```
#include "asm-dom.hpp"
using namespace asmdom;
int main() {
Config config = Config();
init(config);
// asm-dom can be used with a JSX like syntax thanks to gccx
VNode* vnode = (
<div
onclick={[](emscripten::val e) -> bool {
emscripten::val::global("console").call<void>("log", emscripten::val("clicked"));
return true;
}}
>
<span style="font-weight: bold">This is bold</span>
and this is just normal text
<a href="/foo">I'll take you places!</a>
</div>
);
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(
emscripten::val::global("document").call<emscripten::val>(
"getElementById",
std::string("root")
),
vnode
);
// without gccx
VNode* newVnode = h("div",
Data(
Callbacks {
{"onclick", [](emscripten::val e) -> bool {
emscripten::val::global("console").call<void>("log", emscripten::val("another click"));
return true;
}}
}
),
Children {
h("span",
Data(
Attrs {
{"style", "font-weight: normal; font-style: italic"}
}
),
std::string("This is now italic type")
),
h(" and this is just normal text", true),
h("a",
Data(
Attrs {
{"href", "/bar"}
}
),
std::string("I'll take you places!")
)
}
);
// Second `patch` invocation
patch(vnode, newVnode); // asm-dom efficiently updates the old view to the new state
return 0;
};
```
84 changes: 84 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
id: installation
title: Installation
---

In order to use asm-dom you have to prepare your js and C++ environment.

To do this, as first thing, **before importing your compiled code from C++** (wasm or asmjs) you have to include our js file:

- if you are not using [npm](https://www.npmjs.com/package/asm-dom) you can import [our js file](https://github.com/mbasso/asm-dom/blob/master/dist/cpp/asm-dom.js) from [unpkg](https://unpkg.com/asm-dom/dist/cpp/asm-dom.js)

- if you are using [npm](https://www.npmjs.com/package/asm-dom) you can install asm-dom:

```bash
npm install --save asm-dom
```

and import our js file in this way:

```js
import 'asm-dom/cpp/';
```

If you are using this library with webpack you also need to install `arraybuffer-loader`:

```bash
npm install --save-dev arraybuffer-loader
```

and add this object to your loaders:

```js
{
test: /\.wasm$/,
loaders: ['arraybuffer-loader'],
}
```

also, if you have some problems with fs, you can add this to your webpack config:

```js
node: {
fs: 'empty',
},
```

After that, you can build your app using the source code in the [cpp](https://github.com/mbasso/asm-dom/tree/master/cpp) folder:

- `asm-dom.hpp` and `asm-dom-server.hpp`
- `asm-dom.cpp` and `asm-dom-server.cpp` (or `asm-dom.a` that includes both)

and compile it using [emscripten (emcc cli)](http://kripken.github.io/emscripten-site/), [here](http://webassembly.org/getting-started/developers-guide/) is the installation guide. A few tips about this step:

- please make sure to use the `--bind` option during the compilation, otherwise asm-dom will not work.

- emcc has a lot of settings that can optimize the build, we suggest you to see [this](https://kripken.github.io/emscripten-site/docs/optimizing/Optimizing-Code.html) page and [our configuration]((https://github.com/mbasso/asm-dom/tree/master/examples/todomvc%20-%20cpp/package.json)) in the TODOMVC example.

- we suggest you to compile your app to `.bc` and then use it to produce a WebAssembly version and an asm.js version that you can use as fallback

After the compilation you can import your app:

- if you are using webpack, you can see our [example]((https://github.com/mbasso/asm-dom/tree/master/examples/todomvc%20-%20cpp/src/index.js)). In order to import it as a module we have used 2 extra files with emcc `--pre-js` (`prefix.js`) and `--post-js` (`postfix.js`).

- If you want to use wasm without webpack, you can see [this](https://gist.github.com/kripken/59c67556dc03bb6d57052fedef1e61ab) gist.

If you are using [babel](https://babeljs.io/), please make sure to ignore the compiled files, the prefix and suffix:

```js
{
test: /\.js$/,
loaders: ['babel-loader'],
exclude: [
/node_modules/,
/compiled/, // folder that contains the compiled code (wasm and asmjs)
/\.asm\.js$/, // ignore all .asm.js files
/prefix\.js$/, // ignore --pre-js
/postfix\.js$/ // ignore --post-js
],
}
```

You can find a complete example to study in the example folder, [here](https://github.com/mbasso/asm-dom/tree/master/examples/todomvc%20-%20cpp) it is.

If you want to use a [JSX](https://facebook.github.io/jsx/) like syntax in C++ you can also consider using [gccx](https://github.com/mbasso/gccx) before `emcc`, [here](https://github.com/mbasso/asm-dom/tree/master/examples/todomvc%20-%20cpx) you can find the same example with an additional build step.
22 changes: 22 additions & 0 deletions docs/memory-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
id: memory-management
title: Memory Management
---

As we said before the `h` returns a pointer to a VNode, this means that the memory have to be deleted manually. By default asm-dom automatically delete the old vnode from memory when `patch` (or `toHTML`) is called. However, if you want to create a vnode that is not patched, or if you want to manually manage this aspect (setting `clearMemory = false` in the `Config` object to pass to the `init` function), you have to delete it manually.

```c++
VNode* vnode1 = h("div");
VNode* vnode2 = h("div", Children {
h("span")
});
patch(vnode1, vnode2); // vnode1 automatically deleted

VNode* child1 = h(std::string("span"), "child 1");
VNode* child2 = h(std::string("span"), "child 2");
VNode* vnode = h("span", Children {
child1,
child2,
});
delete vnode; // manually delete vnode, child1 and child2 from memory
```
14 changes: 14 additions & 0 deletions docs/motivation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
id: motivation
title: Motivation
---

asm-dom is a minimal WebAssembly virtual DOM to build C++ SPA (Single page applications). You can write an entire SPA in C++ and compile it to WebAssembly (or asmjs as fallback) using [Emscripten](http://kripken.github.io/emscripten-site/), asm-dom will call DOM APIs for you. This will produce an app that `aims to execute at native speed by taking advantage of common hardware capabilities`, also, you can use your C/C++ code without any change, you haven't to create a binding layer to use it (as we have to do if we want to use a C++ lib from JS). Basically we are creating an app in C++ that call javascript if needed instead of the opposite. You can write only once in C++ and share as much code as possible with desktop/mobile apps and web site. If you want to learn more about performance, please see [this](https://github.com/mbasso/asm-dom/tree/master/benchmarks).

## How can I structure my application with asm-dom?*

asm-dom is a low-level virtual DOM library. It is unopinionated with regards to how you should structure your application.

## How did you come up with the concept of asm-dom?*

At the beginning asm-dom is born from the idea to test the powerful of WebAssembly in a common use case that is not gaming, VR, AR or Image / video editing. Unfortunately, at the moment, [GC/DOM Integration](http://webassembly.org/docs/future-features/) is a future feature 🦄, so, asm-dom isn't totally developed in wasm. All interactions with the DOM are written in Javascript. This is a big disadvantage because of the overhead of the binding between JS and WASM, in the future asm-dom will be even more powerful, anyway results are satisfying.
44 changes: 44 additions & 0 deletions docs/patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
id: patch
title: patch
---

The `patch` takes two arguments, the first is a DOM element (you can get it using [emscripten::val::global](https://kripken.github.io/emscripten-site/docs/api_reference/val.h.html)) or a vnode representing the current view. The second is a vnode representing the new, updated view. If `patch` succedeed, the new vnode (the second parameter) is returned.

If a DOM element is passed, `newVnode` will be turned into a DOM node, and the passed element will be replaced by the created DOM node. If an `oldVnode` is passed, asm-dom will efficiently modify it to match the description in the new vnode.

**If `unsafePatch` in `init` is equal to false, any old vnode passed must be the resulting vnode from the previous call to patch. Otherwise, no operation is performed and NULL is returned.**

```c++
VNode* oldVnode = h("span", std::string("old node"));
VNode* newVnode = h("span", std::string("new node"));

patch(
emscripten::val::global("document").call<emscripten::val>("getElementById", emscripten::val("root")),
oldVnode
);
patch(oldVnode, newVnode);

// with unsafePatch = false
VNode* vnode = h("div");
patch(oldVnode, vnode); // returns NULL, found oldVnode, expected newVnode
```
With `unsafePatch = true` you can implement some interesting mechanisms, for example you can do something like this:
```c++
VNode* oldText = h("span", std::string("old text"));
VNode* vnode = h("div", Children {
h("span", std::string("this is a text")),
oldText
});
patch(
emscripten::val::global("document").call<emscripten::val>("getElementById", emscripten::val("root")),
vnode
);
VNode* newText = h(std::string("span"), "new text");
// patch only the child
patch(oldText, newText);
```
Loading

0 comments on commit acb364b

Please sign in to comment.