mise 3.0.0 → 2026.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +0,0 @@
1
- # Components
2
-
3
- Components are how Mise wires together the template, state, and actions to render your application.
4
-
5
- ```javascript
6
- import { dom, component } from 'mise';
7
-
8
- component({
9
- template: state => actions => (
10
- <div>
11
- <span>{state.counter}</span>
12
- <button onclick={actions.increment}>+</button>
13
- <button onclick={actions.decrement}>-</button>
14
- </div>
15
- ),
16
- state: {
17
- counter: 0,
18
- },
19
- actions: {
20
- increment: state => ({ counter: state.counter + 1 }),
21
- decrement: state => ({ counter: state.counter - 1 }),
22
- },
23
- root: document.querySelector('#app'),
24
- });
25
- ```
26
-
27
- #### Applications should only have one root-level component.
28
-
29
- Your application should be structured in a way that only one root level component is ever necessary. Having a single, shared state and a single pool of actions allows you to do some pretty neat things.
30
-
31
- * Load the state of your application from a local storage object
32
- * Hydrate your SSR application with the proper state
33
- * Easily automate bug reporting by passing the whole state and the action called.
34
-
35
- It also forces you to keep your templates presentational. The assumption is that basically no logic should ever live in your templates. Templates can thus be really simple to maintain and to test.
36
-
37
- #### Root items shouldn't have anything else in them.
38
-
39
- Mise doesn't play nice with others. Please make sure your root items dont get anything added/removed to them. Mise works by diffing a VDOM and applying updates to the real DOM. The real DOM isn't the real source of truth, and as such it's possible for Mise to fall out of sync if the DOM changes out of Mise's purview.
40
-
package/docs/lifecycle.md DELETED
@@ -1,71 +0,0 @@
1
- # Lifecycle
2
-
3
- Mise uses a handful of lifecycle methods to allow you fine-grain control of your elements.
4
-
5
- ### `oncreate`
6
-
7
- The `oncreate` function returns the newly created element.
8
-
9
- ```javascript
10
- component({
11
- template: state => actions => (
12
- <div>
13
- <span
14
- oncreate={el => actions.hydrate(el)}
15
- </div>
16
- ),
17
- state: {
18
- // ..
19
- },
20
- actions: {
21
- hydrate: (state, actions, el) => {
22
- // ...
23
- }
24
- }
25
- });
26
- ```
27
-
28
- ### `onremove`
29
-
30
- `onremove` returns a curried function, one containing the actual element and the other containing a `done` function. Once you've finished your business with `onremove`, call `done` to actually remove it from the dom;
31
-
32
- ```javascript
33
- import { removalAnimation } from './animations';
34
-
35
- component({
36
- template: state => actions => (
37
- <div>
38
- <span
39
- onremove={
40
- el => done =>
41
- el.animate([
42
- removalAnimation
43
- ], 2000);
44
-
45
- setTimeout(done, 2000)
46
- }
47
- </div>
48
- ),
49
- });
50
- ```
51
-
52
- ### `onupdate`
53
-
54
- `onupdate` is tricky. Mise calls `onupdate` anytime your element is either set to be replaced or if the props are going to be diffed. Since we can't know if any props have changed until we go through all the props, we'd rather let you know this is coming. To faciliate any change, we'll also pass you the old props so you can determine if you want to run your update.
55
-
56
- ```javascript
57
- component({
58
- template: state => actions => (
59
- <div>
60
- <span
61
- onupdate={
62
- el => oldProps => {
63
- if (oldProps.TTL < state.TTL) {
64
- actions.updateTTL(state.TTL);
65
- }
66
- }
67
- }
68
- </div>
69
- ),
70
- });
71
- ```
package/docs/state.md DELETED
@@ -1,71 +0,0 @@
1
- # State
2
-
3
- State has proven to be a pain-point in more traditional frameworks, so we've worked on making state front-and-center in Mise.
4
-
5
- Every component takes an initial state as one of the required parameters
6
-
7
- ```javascript
8
- import { dom, component } from 'mise';
9
-
10
- component({
11
- template:
12
- //...
13
- actions:
14
- //...
15
- state: {
16
- greeting: 'Hello World!',
17
- },
18
- })
19
- ```
20
-
21
- State is always an object and accessibly in both templates and actions as a parameter.
22
-
23
- #### State is Read-Only.
24
-
25
- You can't ever directly modify the state.
26
-
27
- ```javascript
28
- import { dom, component } from 'mise';
29
- component({
30
- template:
31
- //...
32
- state: {
33
- id: 0
34
- },
35
- actions: {
36
- // wont actually work!
37
- increment: state => { state.id++ },
38
- }
39
- })
40
- ```
41
-
42
- #### State can _only_ be modified by actions
43
-
44
- If you want to modify the state of your application, you can do so via an action. Actions must return a new object which is merged with the current state.
45
-
46
- ```javascript
47
- import { dom, component } from 'mise';
48
- component({
49
- template:
50
- //...
51
- state: {
52
- id: 0
53
- },
54
- actions: {
55
- // works!
56
- increment: state => ({ state.id + 1 }),
57
- }
58
- })
59
- ```
60
-
61
- In this example, after using the increment action, the new state is `1`. After setting a new state, the application re-renders and generates a new template.
62
-
63
- #### Testing
64
- Since state is directly modified by actions, testing an action and having it return a new state is a sufficiently good test.
65
-
66
- ```javascript
67
- describe('increment action', () => {
68
- it('should increment the state by one', () => {
69
- expect(increment({ id: 0 })).toEqual({ id: 1 });
70
- })
71
- })
package/docs/templates.md DELETED
@@ -1,97 +0,0 @@
1
- # Templates
2
-
3
- Mise templates are simple affairs. Primary templates are curried functions that accept state and actions as parameters and return a JSX representation of what your UI looks like. Generally, you only have one primary template.
4
-
5
- ```javascript
6
- const template = state => actions => (
7
- <h1>{state.greeting}</h1>
8
- )
9
- ```
10
-
11
- Secondary templates (i.e. templates that aren't at the root level) are functions that accept props and children as their parameters.
12
-
13
- ```javascript
14
- const secondary = (props, children) => {
15
- <li className={props.className}>
16
- <div>{children}</div>
17
- </li>
18
- }
19
- ```
20
-
21
- #### All templates in Mise are presentational templates.
22
- Templates don't contain actual logic in them. They instead contain references to functions that contain the actual logic. The closest a template will get to logic is to pass in parameters that a function will need.
23
-
24
- ```javascript
25
- const template => state => actions => (
26
- <div id="click-counter">
27
- <span>{state.clicked}</span>
28
- <button onclick={actions.increment}>Click me!</span>
29
- <button onclick={e => actions.reset(e)}>Reset</span>
30
- </div>
31
- )
32
- ```
33
-
34
- #### Templates in Mise are composable
35
-
36
- ```javascript
37
- const template = state => actions => (
38
- <div id="todos">
39
- <h1>Todos!</h1>
40
- <ul>
41
- {state.todos.map(todo =>
42
- <TodoItem
43
- id={todo.id}
44
- text={todo.text}
45
- complete={actions.complete}
46
- remove={actions.remove} />
47
- )}
48
- </ul>
49
- </div>
50
- )
51
-
52
- const TodoItem = ({
53
- id,
54
- text,
55
- complete,
56
- remove
57
- }) => (
58
- <li>
59
- <span onclick={() => complete(id)}>
60
- {todo.text}
61
- </span>
62
- <button onclick={() => remove(id)}>
63
- x
64
- </button>
65
- </li>
66
- )
67
- ```
68
-
69
- By composing templates, we can keep items compartmentalized and easy to test. This also reduces the cognative load of onboarding a new developer - it's easy to see the hierarchy.
70
-
71
- #### Testing
72
-
73
- Since Mise components are all pure functions, testing them is simple. We use Jest and strongly suggest that you use it to test Mise.
74
-
75
- ```javascript
76
- import { createElement } from 'mise/src/vdom';
77
-
78
- import { template } from './src/todo.template';
79
- import { actions } from './src/todo.actions';
80
- import { state } from './src/todo.state';
81
-
82
- describe('template test', () => {
83
- const { body } = document;
84
-
85
- beforeEach(() => {
86
- body.innerHTML = '';
87
- })
88
-
89
- it('should render a todo ul', () => {
90
- /* this is basically what mise does, too! */
91
- const result = template(state)(actions);
92
- body.appendChild(createElement(result));
93
-
94
- expect(body.querySelector('ul')).not.toBeNull();
95
- })
96
- })
97
- ```
@@ -1,18 +0,0 @@
1
- import { dom, component } from 'mise';
2
-
3
- component({
4
- template: state => actions => (
5
- <div>
6
- <span>{state.counter}</span>
7
- <button onclick={actions.increment}>+</button>
8
- <button onclick={actions.decrement}>-</button>
9
- </div>
10
- ),
11
- state: {
12
- counter: 0,
13
- },
14
- actions: {
15
- increment: state => ({ counter: state.counter + 1 }),
16
- decrement: state => ({ counter: state.counter - 1 }),
17
- }
18
- });
@@ -1,16 +0,0 @@
1
- import { spanStyle } from './todo.style';
2
-
3
- const TodoItem = ({ id, text, done, toggle, remove }) =>
4
- <li>
5
- <div
6
- onclick={() => toggle({id})}
7
- style={done ? {textDecoration: 'line-through' } : {}}>
8
- {text}
9
- </div>
10
- <span
11
- onclick={() => remove({ id })}
12
- style={spanStyle}>
13
- x</span>
14
- </li>;
15
-
16
- export { TodoItem };
@@ -1,6 +0,0 @@
1
- const spanStyle = {
2
- marginLeft: '10px',
3
- color: 'red',
4
- };
5
-
6
- export { spanStyle };
@@ -1,36 +0,0 @@
1
- const add = (state, actions) => {
2
- const text = state.input;
3
- actions.clearInput();
4
- return {
5
- todos: [
6
- ...state.todos,
7
- {
8
- text,
9
- id: state.id,
10
- done: false
11
- }
12
- ],
13
- id: state.id + 1,
14
- };
15
- };
16
-
17
- const input = (state, actions, { value }) => ({ input: value });
18
-
19
- const toggle = (state, actions, { id }) => ({
20
- todos: state.todos.map(todo => todo.id === id ? Object.assign({}, todo, { done: !todo.done }) : todo),
21
- });
22
-
23
- const remove = (state, actions, { id }) => ({ todos: state.todos.filter(todo => todo.id !== id )});
24
-
25
- const clearInput = () => ({ input: '' });
26
-
27
- const clearTodos = () => ({ todos : [] });
28
-
29
- export {
30
- add,
31
- input,
32
- toggle,
33
- remove,
34
- clearInput,
35
- clearTodos,
36
- };
@@ -1,11 +0,0 @@
1
- import { dom, component } from 'mise';
2
-
3
- import { template } from './todos.template';
4
- import { state } from './todos.state';
5
- import * as actions from './todos.actions';
6
-
7
- component({
8
- template,
9
- state,
10
- actions,
11
- });
@@ -1,7 +0,0 @@
1
- const state = {
2
- todos: [],
3
- input: '',
4
- id: 0,
5
- };
6
-
7
- export { state };
@@ -1,35 +0,0 @@
1
- import { TodoItem } from './todo/todo.template';
2
-
3
- const template = state => actions => (
4
- <div>
5
- <h1>Todos</h1>
6
- <ul>
7
- {state.todos.map(
8
- todo =>
9
- <TodoItem
10
- text={todo.text}
11
- id={todo.id}
12
- done={todo.done}
13
- toggle={actions.toggle}
14
- remove={actions.remove}/>
15
- )}
16
- </ul>
17
-
18
- <input
19
- type="text"
20
- value={state.input}
21
- oninput= {
22
- e => actions.input({ value: e.target.value })
23
- }/>
24
- <button
25
- onclick={actions.add}>
26
- Add Todo
27
- </button>
28
- <button
29
- onclick={actions.clearTodos}>
30
- Clear All Todos
31
- </button>
32
- </div>
33
- );
34
-
35
- export { template };
package/src/component.js DELETED
@@ -1,80 +0,0 @@
1
- import { reconciler } from './vdom/reconciler';
2
- import { create } from './vdom/fiber';
3
-
4
- const component = ({
5
- template,
6
- state,
7
- actions,
8
- root = document.body,
9
- }) => {
10
- const { add } = reconciler();
11
-
12
- let appState;
13
- let appTemplate;
14
- let appActions;
15
-
16
- const generateTemplate = unsafeState => (unsafeActions) => {
17
- const readOnlyState = { ...unsafeState };
18
- const readOnlyActions = { ...unsafeActions };
19
-
20
- return template(readOnlyState)(readOnlyActions);
21
- };
22
-
23
- const render = () => {
24
- const previousTemplate = appTemplate;
25
- appTemplate = generateTemplate(appState)(appActions);
26
-
27
- const fiber = create({
28
- parent: root,
29
- element: root.childNodes[0],
30
- previous: previousTemplate,
31
- next: appTemplate,
32
- });
33
-
34
- add(fiber);
35
- };
36
-
37
- const update = (partialState) => {
38
- appState = {
39
- ...appState,
40
- ...partialState,
41
- };
42
-
43
- render();
44
- };
45
-
46
- const bindUpdateToActions = (unboundActions) => {
47
- const tempActions = {};
48
-
49
- for (const [key, fn] of Object.entries(unboundActions)) {
50
- tempActions[key] = (data) => {
51
- const result = fn(appState, appActions, data);
52
-
53
- if (typeof result === 'function') {
54
- result(update);
55
- } else {
56
- update(result);
57
- }
58
- };
59
- }
60
-
61
- return tempActions;
62
- };
63
-
64
- const init = (initialActions) => {
65
- appState = state;
66
- appActions = bindUpdateToActions(initialActions);
67
- appTemplate = generateTemplate(appState)(appActions);
68
-
69
- const fiber = create({
70
- parent: root,
71
- next: appTemplate,
72
- });
73
-
74
- add(fiber);
75
- };
76
-
77
- init(actions);
78
- };
79
-
80
- export { component };
package/src/dom.js DELETED
@@ -1,27 +0,0 @@
1
- const dom = (type, uncheckedProps = {}, ...uncheckedChildren) => {
2
- let children = [];
3
- let props = uncheckedProps;
4
-
5
- for (const child of uncheckedChildren) {
6
- if (Array.isArray(child)) {
7
- children = [...children, ...child];
8
- } else {
9
- children = [...children, typeof child === 'number' ? String(child) : child];
10
- }
11
- }
12
-
13
- if (uncheckedProps === null) { props = {}; }
14
-
15
- if (props.className) {
16
- props.class = props.className;
17
- props.className = undefined;
18
- }
19
-
20
- if (typeof type === 'function') {
21
- return type(props, children);
22
- }
23
-
24
- return { type, props, children };
25
- };
26
-
27
- export { dom };
package/src/index.js DELETED
@@ -1,2 +0,0 @@
1
- export { component } from './component';
2
- export { dom } from './dom';
package/src/utils.js DELETED
@@ -1,4 +0,0 @@
1
- const getUniques = (first, second) =>
2
- new Set([...Object.keys(first || {}), ...Object.keys(second || {})]);
3
-
4
- export { getUniques };
package/src/vdom/fiber.js DELETED
@@ -1,30 +0,0 @@
1
- const types = {
2
- create: 'CREATE',
3
- remove: 'REMOVE',
4
- replace: 'REPLACE',
5
- update: 'UPDATE',
6
- };
7
-
8
- const empty = () => ({
9
- children: [],
10
- empty: true,
11
- });
12
-
13
- const create = ({
14
- parent,
15
- element = null,
16
- previous = empty(),
17
- next = empty(),
18
- }) => ({
19
- parent,
20
- previous: {
21
- tree: previous,
22
- element,
23
- },
24
- next: {
25
- tree: next,
26
- element: null,
27
- },
28
- });
29
-
30
- export { create, types };
@@ -1,118 +0,0 @@
1
- import { create, types } from './fiber';
2
- import { createElement, paint } from './vdom';
3
-
4
- const reconciler = () => {
5
- let inProgress = false;
6
- let workQueue = [];
7
- let finished = [];
8
-
9
- const next = () => {
10
- const [item, ...rest] = workQueue;
11
- workQueue = rest;
12
- return item;
13
- };
14
-
15
- const shouldReplace = (previous, updated) =>
16
- typeof previous !== typeof updated ||
17
- (typeof previous === 'string' && previous !== updated) ||
18
- previous.type !== updated.type;
19
-
20
- const addChildren = (fiber) => {
21
- const prevChildren = (fiber.previous.tree && fiber.previous.tree.children) || [];
22
- const nextChildren = (fiber.next.tree && fiber.next.tree.children) || [];
23
- const length = Math.max(prevChildren.length, nextChildren.length);
24
-
25
- if (fiber.previous.element) {
26
- for (let i = 0; i < length; i += 1) {
27
- const newFiber = create({
28
- parent: fiber.previous.element,
29
- element: (fiber.previous.element && fiber.previous.element.childNodes[i]) || null,
30
- previous: prevChildren[i],
31
- next: nextChildren[i],
32
- });
33
-
34
- // eslint-disable-next-line no-use-before-define
35
- add(newFiber);
36
- }
37
- }
38
- };
39
-
40
- const reconcile = (fiber) => {
41
- addChildren(fiber);
42
-
43
- if (!fiber.previous.tree || fiber.previous.tree.empty) {
44
- fiber.action = types.create;
45
- fiber.next.element = createElement(fiber.next.tree);
46
-
47
- if (fiber.next.tree.props && fiber.next.tree.props.oncreate) {
48
- fiber.lifecycle = fiber.next.tree.props.oncreate;
49
- }
50
-
51
- return fiber;
52
- }
53
-
54
- if (!fiber.next.tree || fiber.next.tree.empty) {
55
- fiber.action = types.remove;
56
- if (fiber.previous.tree.props && fiber.previous.tree.props.onremove) {
57
- fiber.lifecycle = fiber.previous.tree.props.onremove;
58
- }
59
-
60
- return fiber;
61
- }
62
-
63
- if (shouldReplace(fiber.previous.tree, fiber.next.tree)) {
64
- fiber.action = types.replace;
65
- fiber.next.element = createElement(fiber.next.tree);
66
-
67
- if (fiber.next.tree.props && fiber.next.tree.props.onupdate) {
68
- fiber.lifecycle = fiber.next.tree.props.onupdate;
69
- }
70
-
71
- return fiber;
72
- }
73
-
74
- fiber.action = types.update;
75
- if (fiber.next.tree.props && fiber.next.tree.props.onupdate) {
76
- fiber.lifecycle = fiber.next.tree.props.onupdate;
77
- }
78
-
79
- return fiber;
80
- };
81
-
82
- const work = (deadline) => {
83
- while (deadline.timeRemaining() && workQueue.length) {
84
- const completed = reconcile(next());
85
- finished = [...finished, completed];
86
- }
87
- };
88
-
89
- const process = (deadline) => {
90
- work(deadline);
91
-
92
- if (workQueue.length) {
93
- requestIdleCallback(process);
94
- } else {
95
- inProgress = false;
96
-
97
- const boundPaint = paint.bind(null, finished);
98
- requestAnimationFrame(boundPaint);
99
-
100
- finished = [];
101
- }
102
- };
103
-
104
- const add = (fiber) => {
105
- workQueue = [...workQueue, fiber];
106
-
107
- if (!inProgress) {
108
- inProgress = true;
109
- requestIdleCallback(process);
110
- }
111
- };
112
-
113
- return {
114
- add,
115
- };
116
- };
117
-
118
- export { reconciler };