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.
- package/README.md +217 -82
- package/installArchSpecificPackage.js +57 -0
- package/package.json +24 -49
- package/.babelrc +0 -9
- package/.eslintrc.js +0 -30
- package/.travis.yml +0 -11
- package/LICENSE +0 -21
- package/changelist.md +0 -24
- package/dist/mise.js +0 -1
- package/docs/actions.md +0 -96
- package/docs/components.md +0 -40
- package/docs/lifecycle.md +0 -71
- package/docs/state.md +0 -71
- package/docs/templates.md +0 -97
- package/example/counter/counter.jsx +0 -18
- package/example/todos/todo/todo.jsx +0 -16
- package/example/todos/todo/todo.style.js +0 -6
- package/example/todos/todos.actions.js +0 -36
- package/example/todos/todos.component.js +0 -11
- package/example/todos/todos.state.js +0 -7
- package/example/todos/todos.template.jsx +0 -35
- package/src/component.js +0 -80
- package/src/dom.js +0 -27
- package/src/index.js +0 -2
- package/src/utils.js +0 -4
- package/src/vdom/fiber.js +0 -30
- package/src/vdom/reconciler.js +0 -118
- package/src/vdom/vdom.js +0 -141
- package/test/e2e/counter.test.js +0 -85
- package/test/e2e/todos.test.js +0 -263
- package/test/unit/component.test.js +0 -80
- package/test/unit/dom.test.js +0 -111
- package/test/unit/utils.test.js +0 -26
- package/test/unit/vdom/fiber.test.js +0 -59
- package/test/unit/vdom/reconciler.test.js +0 -208
- package/test/unit/vdom/vdom.test.js +0 -331
- package/test/utils.js +0 -62
- package/tsconfig.json +0 -15
- package/webpack.config.js +0 -39
package/docs/components.md
DELETED
|
@@ -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,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,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
package/src/utils.js
DELETED
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 };
|
package/src/vdom/reconciler.js
DELETED
|
@@ -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 };
|