Awesome
The Merlin JS framework
A functional JS framework that values elegance, simplicity and minimalism.
State Management + vDom + Template Engine + SPA Router = ❤️
❤️ Features
- No building tools.
- Single HTML file by default.
- Built by combining ideas from small modules following the UNIX philosophy.
- Pure functional ELM architecture state management library.
- Ultrafast vDom.
- Server side rendered by default (templates are valid html).
- Built-in Single Page Application Router.
- Ridiculously small API. After reading this file you will understand
Merlin
better than me.
💡 Showcase
<!DOCTYPE html>
<html>
<head>
<title>Todo - The Merlin JS framework</title>
</head>
<body>
<main>
<h1>To do list</h1>
<input type="text" value:="value" oninput:="NewValue">
<ul>
<li each:="todos" text:></li>
</ul>
<button onclick:="AddTodo">New!</button>
</main>
<script type="module">
import {app} from "https://cdn.jsdelivr.net/gh/marcodpt/merlin/index.min.js"
app({
node: document.body.querySelector('main'),
init: {
value: '',
todos: []
},
register: update => ({
NewValue: ev => update(state => ({
...state,
value: ev.target.value
})),
AddTodo: () => update(({todos, value}) => ({
todos: todos.concat(value),
value: ''
}))
}),
view: (state, events) => ({
...state,
...events
})
})
</script>
</body>
</html>
💯 Examples
📖 API
Merlin
uses Tint as its template engine,
you should read the docs
for a complete reference.
app({node, template?, view?, init, register}) => stop
node: DOM Node
Where to mount the app
.
template: Dom Node
An optional template
to render, if nothing is passed the node
itself will
be used.
view: (state, events) => data
Exactly as defined in Ring.
The only exception is that it returns data
that will be used for
Tint to render the page,
if not passed the unmodified state
will be returned to
Tint.
init: _
Exactly as defined in Ring.
The initial state
of the app
. It can be any type of data.
register: (update, dispatch) => events
Exactly as defined in Ring.
It is called before initializing the app
returning the registered
events
.
stop: () => ()
Exactly as defined in Ring.
Returns a function that stops
the app
.
spa({node, routes, plugins?}) => stop
node: DOM Node
Where to mount the spa
.
routes: {route: {template?, init?, view?, register?}}
route: string
Exactly as defined in Wand.
Accepts *
to match any path and :param
to declare variable.
template: Dom Node
A template to be rendered on the route, if nothing is passed it will use the
original content of the node
.
init: routeData => state
An optional function that will be called every time the route is started,
returning the initial state. If not passed, Params
from routeData
will be
used as the initial state
.
view, register
Exactly as defined in app
plugins: [routeData => ()]
Exactly as defined in Wand.
An optional array of plugins
, which are executed sequentially with each
route
change
and which can modify the routeData
.
stop: () => ()
Exactly as defined in Wand.
Returns a function that stops the spa
.
routeData {url, route, path, Params, query, Query}
Exactly as defined in Wand.
Plugins
can introduce new properties or change existing ones.
url: string
The url
as it was passed.
route: string
The route
that matched as declared.
path: string
The part of the url
before the ?
.
Params: object
Object containing the variables declared in the route
with the associated
values in the current path
.
query: string
The part of url
after the ?
.
Query: object
Parsed query string.
🤝 Contributing
It's a very simple project. Any contribution, any feedback is greatly appreciated.
⭐ Support
If this project was useful to you, consider giving it a star on github, it's a way to increase evidence and attract more contributors.
🙏 Acknowledgment
This work is hugely influenced by these amazing projects:
A huge thank you to all the people who contributed to these projects.