Awesome
The Merlin JS framework
A functional JS framework that values elegance, simplicity and minimalism.
Raj + Superfine + Tint + SPA Router = ❤️
❤️ Features
- No building tools.
- Single HTML file by default.
- Pure functional ELM architecture state management library.
- Ultrafast vDom.
- Built-in Single Page Application Router.
- Server side rendered by default (templates are valid html).
- 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: []
}],
update: (todo, state) => {
if (todo != null) {
state.value = todo
} else if (state.value) {
state.todos.push(state.value)
state.value = ""
}
return [state]
},
view: (state, dispatch) => ({
...state,
AddTodo: () => dispatch(),
NewValue: ev => dispatch(ev.target.value)
})
})
</script>
</body>
</html>
💯 Examples
- Counter: Demo Source
- Todo SSR: Demo Source
- Clock: Demo Source
- Stopwatch: Demo Source
- Table: Demo Source
- Components: Demo Source
- SPA Router: Demo HTML JS
📖 API
Merlin
uses Raj as its state management
library, you should read the
docs for a complete reference.
Merlin
uses Tint as its template engine,
you should read the docs
for a complete reference.
app({node, template?, view?, init, update, done?}) => stop
node
DOM Node: Where to mount theapp
.template
Dom Node: An optionaltemplate
to render, if nothing is passed thenode
itself will be used.init
[state, effect?]: Exactly as defined in Raj.state
: The initial state of theapp
. It can be any type of data.effect
(dispatch) => (): Optional function that introduces side effects.dispatch
(message) => (): Function that triggers an update on the state.
update
(message, state) => [newState, effect?]: Exactly as defined in Raj.message
: The context of the update. It can be any type of data.state
: The current state when update was called. It can be any type of data.newState
: The new state of theapp
. It can be any type of data.
view
(state, dispatch) => data: An optional function that formats thestate
(and eventually applieseffects
) and is passed directly to Tint for rendering. If omitted,state
will be used without modifications.done
state => (): Exactly as defined in Raj. Optional function that will be called to end theapp
.stop
() => (): Returns a function that stops theapp
.
spa({node, routes, plugins?}) => stop
node
DOM Node: Where to mount thespa
.routes
{route: {template?, init?, view, update, done}}:route
string: 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 thenode
.init
routeData => [state, effect?]: An optional function that will be called every time the route is started, returning the initial state. If not passed,Params
fromrouteData
will be used as the initialstate
.view
,update
,done
: Exactly as defined inapp
plugins
[routeData => {...newData, ...routeData}]: An optional array of plugins, which are executed sequentially with each route change and must return a object whose properties will be attached torouteData
.stop
() => (): Returns a function that stops thespa
.
routeData {url, route, path, Params, query, Query, old, ...newData}
url
string: Theurl
as it was passed.route
string: Theroute
that matched as declared.path
string: The part of theurl
before the?
.Params
Object: Object containing the variables declared in theroute
with the associated values in the currentpath
.query
string: The part ofurl
after the?
.Query
Object: Parsed query string.old
{url, route, path, Params, query}: PreviousrouteData
ornull
.newData
New properties introduced by plugins.
🤝 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.