Skip to content

Commit 6b14dfd

Browse files
authored
[refactor] rewrite WebCell 3 core API with latest ES/TS, MobX & vDOM (#17)
1 parent af0a77d commit 6b14dfd

26 files changed

+2269
-2243
lines changed

.github/workflows/push.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Commit preview
2+
on:
3+
push:
4+
branches-ignore:
5+
- main
6+
jobs:
7+
Build-and-Deploy:
8+
env:
9+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
10+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
11+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v3
15+
16+
- uses: pnpm/action-setup@v2
17+
with:
18+
version: 8
19+
- uses: actions/setup-node@v3
20+
with:
21+
node-version: 18
22+
cache: pnpm
23+
- name: Install & Build
24+
run: |
25+
pnpm i --frozen-lockfile
26+
pnpm build
27+
28+
- uses: amondnet/vercel-action@v25
29+
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
30+
with:
31+
vercel-token: ${{ secrets.VERCEL_TOKEN }}
32+
github-token: ${{ secrets.GITHUB_TOKEN }}
33+
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
34+
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
35+
working-directory: ./docs

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ yarn.lock
44
dist/
55
.parcel-cache/
66
docs/
7-
.vscode/settings.json
7+
.vscode/settings.json
8+
.vercel

.npmignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
.eslintrc.json
22
jest.config.ts
33
test/
4+
preview/
45
.parcel-cache/
56
Contributing.md
67
docs/
78
.husky/
89
.github/
9-
.vscode/
10+
.vscode/
11+
.vercel/

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
auto-install-peers = false

Migrating.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@ import {
1616
component,
1717
+ observer,
1818
- mixin,
19-
+ WebCell,
20-
createCell,
21-
Fragment
19+
- createCell,
20+
- Fragment
2221
} from 'web-cell';
2322
+import { observable } from 'mobx';
2423

2524
-interface State {
2625
+class State {
2726
+ @observable
28-
key: string;
27+
- key: string;
28+
+ accessor key = '';
2929
}
3030

3131
@component({
3232
tagName: 'my-tag'
3333
})
3434
+@observer
3535
-export class MyTag extends mixin<{}, State>() {
36-
+export class MyTag extends WebCell() {
36+
+export class MyTag extends HTMLElement {
3737
- state: Readonly<State> = {
3838
- key: 'value'
3939
- };
@@ -57,7 +57,7 @@ At the same time, `shouldUpdate() {}` life-cycle has been dropped. You just need
5757
MobX's [`@observable`][4] & [`reaction()`][5] are awesome APIs to implement these above with clear codes, so we add `mobx` package as a dependency:
5858

5959
```shell
60-
npm install mobx@5
60+
npm install mobx
6161
```
6262

6363
On the other hand, [`mobx-web-cell` adapter][6] has been merged into the core package. And cause of replacing **Prototype Overwrite** with **Class Inheritance** to refactor **Class Mixins**, `@observer` decorator should follow strict order to make observation work:
@@ -70,9 +70,8 @@ import {
7070
- watch,
7171
+ observer,
7272
- mixin,
73-
+ WebCell,
74-
createCell,
75-
Fragment
73+
- createCell,
74+
- Fragment
7675
} from 'web-cell';
7776
-import { observer } from 'mobx-web-cell';
7877
+import { observable } from 'mobx';
@@ -86,11 +85,14 @@ export interface MyTagProps extends WebCellProps {
8685
})
8786
@observer
8887
-export class MyTag extends mixin<MyTagProps>() {
89-
+export class MyTag extends WebCell<MyTagProps>() {
88+
+export class MyTag extends HTMLElement {
89+
+ declare props: MyTagProps;
90+
9091
@attribute
9192
- @watch
9293
+ @observable
93-
count = 0;
94+
- count = 0;
95+
+ accessor count = 0;
9496

9597
- render({ count }: MyTagProps) {
9698
+ render() {
@@ -109,15 +111,14 @@ export interface MyTagProps extends WebCellProps {
109111
import {
110112
component,
111113
- mixin
112-
+ WebCell
113114
} from 'web-cell';
114115

115116
@component({
116117
tagName: 'my-tag',
117118
- renderTarget: 'children'
118119
})
119120
-export class MyTag extends mixin() {
120-
+export class MyTag extends WebCell() {
121+
+export class MyTag extends HTMLElement {
121122
}
122123
```
123124

@@ -127,7 +128,6 @@ import {
127128
import {
128129
component,
129130
- mixin
130-
+ WebCell
131131
} from 'web-cell';
132132

133133
@component({
@@ -136,7 +136,7 @@ import {
136136
+ mode: 'open'
137137
})
138138
-export class MyTag extends mixin() {
139-
+export class MyTag extends WebCell() {
139+
+export class MyTag extends HTMLElement {
140140
}
141141
```
142142

@@ -149,7 +149,6 @@ This makes **Shadow CSS** to react with the data of component instances.
149149
import {
150150
component,
151151
- mixin
152-
+ WebCell
153152
} from 'web-cell';
154153

155154
@component({
@@ -163,7 +162,7 @@ import {
163162
- }
164163
})
165164
-export class MyTag extends mixin() {
166-
+export class MyTag extends WebCell() {
165+
+export class MyTag extends HTMLElement {
167166
render() {
168167
return <>
169168
+ <style>
@@ -183,14 +182,15 @@ import {
183182

184183
[JSDoc's `@deprecated` hints][7] will lead your way to rename them:
185184

186-
1. `mixin()` => `WebCell()`
185+
1. `mixin()` => `HTMLElement` & its Sub-classes
187186
2. `mixinForm()` => `WebField()`
188-
3. `@watch` => `@observable`
187+
3. `@watch` => `@observable accessor`
189188

190189
## Appendix: v3 prototype
191190

192-
1. https://codesandbox.io/s/web-components-jsx-i7u60?file=/index.tsx
193-
2. https://codesandbox.io/s/mobx-lite-791eg?file=/src/index.ts
191+
1. [Legacy architecture](https://codesandbox.io/s/web-components-jsx-i7u60?file=/index.tsx)
192+
2. [Modern architecture](https://codesandbox.io/s/mobx-web-components-pvn9rf?file=/src/WebComponent.ts)
193+
3. [MobX lite](https://codesandbox.io/s/mobx-lite-791eg?file=/src/index.ts)
194194

195195
[1]: https://github.com/mobxjs/mobx/blob/mobx4and5/docs/refguide/observer-component.md#local-observable-state-in-class-based-components
196196
[2]: https://blog.cloudboost.io/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e

ReadMe.md

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
[Web Components][1] engine based on VDOM, [JSX][2], [MobX][12] & [TypeScript][3]
66

7-
[![NPM Dependency](https://david-dm.org/EasyWebApp/WebCell.svg)][4]
7+
[![NPM Dependency](https://img.shields.io/librariesio/github/EasyWebApp/WebCell.svg)][4]
88
[![CI & CD](https://github.com/EasyWebApp/WebCell/actions/workflows/main.yml/badge.svg)][5]
99

1010
[![Anti 996 license](https://img.shields.io/badge/license-Anti%20996-blue.svg)][6]
@@ -17,21 +17,38 @@
1717

1818
[![NPM](https://nodei.co/npm/web-cell.png?downloads=true&downloadRank=true&stars=true)][11]
1919

20+
## Feature
21+
22+
### Engines comparison
23+
24+
| feature | WebCell 3 | WebCell 2 | React | Vue |
25+
| :-----------: | :------------------: | :------------------: | :---------------------------: | :---------------------------------: |
26+
| JS language | TypeScript 5 | TypeScript 4 | ECMAScript or TypeScript | ECMAScript or TypeScript |
27+
| JS syntax | ES decorator stage-3 | ES decorator stage-2 | | |
28+
| XML syntax | JSX import | JSX factory | JSX factory/import | HTML/Vue template or JSX (optional) |
29+
| DOM API | Web components | Web components | HTML 5+ | HTML 5+ |
30+
| view renderer | DOM renderer 2 | SnabbDOM | (built-in) | SnabbDOM (forked) |
31+
| state API | MobX `@observable` | `this.state` | `this.state` or `useState()` | `this.$data` or `ref()` |
32+
| props API | MobX `@observable` | `@watch` | `this.props` or `props => {}` | `this.$props` or `defineProps()` |
33+
| state manager | MobX 6+ | MobX 4/5 | Redux | VueX |
34+
| page router | JSX tags | JSX tags + JSON data | JSX tags | JSON data |
35+
| asset bundler | Parcel 2 | Parcel 1 | webpack | Vite |
36+
2037
## Usage
2138

2239
Demo & **GitHub template**: https://web-cell.dev/scaffold/
2340

2441
### Project bootstrap
2542

26-
Command
43+
#### Command
2744

2845
```shell
2946
npm init -y
30-
npm install web-cell
31-
npm install parcel -D
47+
npm install dom-renderer mobx web-cell
48+
npm install parcel @parcel/config-default "@parcel/transformer-typescript-tsc" -D
3249
```
3350

34-
`package.json`
51+
#### `package.json`
3552

3653
```json
3754
{
@@ -42,18 +59,37 @@ npm install parcel -D
4259
}
4360
```
4461

45-
[`tsconfig.json`](https://github.com/EasyWebApp/WebCell/blob/main/tsconfig.json)
62+
#### `tsconfig.json`
63+
64+
```json
65+
{
66+
"compilerOptions": {
67+
"target": "ES6",
68+
"moduleResolution": "Node",
69+
"useDefineForClassFields": true,
70+
"jsx": "react-jsx",
71+
"jsxImportSource": "dom-renderer"
72+
}
73+
}
74+
```
4675

47-
`source/index.html`
76+
#### `.parcelrc`
77+
78+
```json
79+
{
80+
"extends": "@parcel/config-default",
81+
"transformers": {
82+
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
83+
}
84+
}
85+
```
86+
87+
#### `source/index.html`
4888

4989
```html
50-
<script
51-
crossorigin
52-
src="https://polyfill.app/api/polyfill?features=es.array.flat,es.object.from-entries"
53-
></script>
54-
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/[email protected]/webcomponents-bundle.min.js"></script>
55-
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/[email protected]/custom-elements-es5-adapter.js"></script>
56-
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js"></script>
90+
<script src="https://polyfill.web-cell.dev/feature/ECMAScript.js"></script>
91+
<script src="https://polyfill.web-cell.dev/feature/WebComponents.js"></script>
92+
<script src="https://polyfill.web-cell.dev/feature/ElementInternals.js"></script>
5793

5894
<script src="source/SubTag.tsx"></script>
5995
<script src="source/TestTag.tsx"></script>
@@ -67,16 +103,16 @@ npm install parcel -D
67103
`source/SubTag.tsx`
68104

69105
```jsx
70-
import { WebCellProps, WebCell, createCell, component } from 'web-cell';
106+
import { WebCellProps, component } from 'web-cell';
71107

72-
export function InlineTag({ defaultSlot }: WebCellProps) {
73-
return <span>{defaultSlot}</span>;
108+
export function InlineTag({ children }: WebCellProps) {
109+
return <span>{children}</span>;
74110
}
75111

76112
@component({
77113
tagName: 'sub-tag'
78114
})
79-
export class SubTag extends WebCell() {
115+
export class SubTag extends HTMLElement {
80116
render() {
81117
return <InlineTag>test</InlineTag>;
82118
}
@@ -87,17 +123,8 @@ export class SubTag extends WebCell() {
87123

88124
`source/TestTag.tsx`
89125

90-
```typescript
91-
import {
92-
WebCellProps,
93-
WebCell,
94-
createCell,
95-
component,
96-
attribute,
97-
observer,
98-
on,
99-
Fragment
100-
} from 'web-cell';
126+
```tsx
127+
import { WebCellProps, component, attribute, observer, on } from 'web-cell';
101128
import { observable } from 'mobx';
102129

103130
import { SubTag } from './SubTag';
@@ -108,18 +135,20 @@ export interface TestTagProps extends WebCellProps {
108135

109136
class State {
110137
@observable
111-
status = '';
138+
accessor status = '';
112139
}
113140

114141
@component({
115142
tagName: 'test-tag',
116143
mode: 'open'
117144
})
118145
@observer
119-
export class TestTag extends WebCell<TestTagProps>() {
146+
export class TestTag extends HTMLElement {
147+
declare props: TestTagProps;
148+
120149
@attribute
121150
@observable
122-
topic = 'Test';
151+
accessor topic = 'Test';
123152

124153
state = new State();
125154

@@ -167,7 +196,7 @@ export class TestTag extends WebCell<TestTagProps>() {
167196
- [Element Internals](https://web.dev/more-capable-form-controls/)
168197
- [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables)
169198
- [ECMAScript 6+](http://es6-features.org/)
170-
- [TypeScript 4+][3]
199+
- [TypeScript 5+][3]
171200

172201
## Life Cycle hooks
173202

@@ -233,7 +262,7 @@ We recommend these libraries to use with WebCell:
233262
[1]: https://www.webcomponents.org/
234263
[2]: https://facebook.github.io/jsx/
235264
[3]: https://www.typescriptlang.org
236-
[4]: https://david-dm.org/EasyWebApp/WebCell
265+
[4]: https://libraries.io/npm/web-cell
237266
[5]: https://github.com/EasyWebApp/WebCell/actions/workflows/main.yml
238267
[6]: https://github.com/996icu/996.ICU/blob/master/LICENSE
239268
[7]: https://github.com/jaywcjlove/awesome-uikit

source/Async.tsx renamed to legacy/Async.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { observable } from 'mobx';
22

33
import { ComponentTag, WebCellProps, FunctionComponent } from './utility';
4-
import { WebCellClass, WebCell } from './WebCell';
5-
import { component, observer, reaction } from './decorator';
4+
import { WebCellClass, WebCell } from '../source/WebCell';
5+
import { component, observer, reaction } from '../source/decorator';
66
import { createCell } from './renderer';
77

88
export interface AsyncBoxProps extends WebCellProps {

0 commit comments

Comments
 (0)