Skip to content

Commit

Permalink
[refactor] rewrite WebCell 3 core API with latest ES/TS, MobX & vDOM (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
TechQuery authored Jan 5, 2024
1 parent af0a77d commit 6b14dfd
Show file tree
Hide file tree
Showing 26 changed files with 2,269 additions and 2,243 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Commit preview
on:
push:
branches-ignore:
- main
jobs:
Build-and-Deploy:
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: pnpm
- name: Install & Build
run: |
pnpm i --frozen-lockfile
pnpm build
- uses: amondnet/vercel-action@v25
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: ./docs
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ yarn.lock
dist/
.parcel-cache/
docs/
.vscode/settings.json
.vscode/settings.json
.vercel
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
.eslintrc.json
jest.config.ts
test/
preview/
.parcel-cache/
Contributing.md
docs/
.husky/
.github/
.vscode/
.vscode/
.vercel/
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
auto-install-peers = false
42 changes: 21 additions & 21 deletions Migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ import {
component,
+ observer,
- mixin,
+ WebCell,
createCell,
Fragment
- createCell,
- Fragment
} from 'web-cell';
+import { observable } from 'mobx';

-interface State {
+class State {
+ @observable
key: string;
- key: string;
+ accessor key = '';
}

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

```shell
npm install mobx@5
npm install mobx
```

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:
Expand All @@ -70,9 +70,8 @@ import {
- watch,
+ observer,
- mixin,
+ WebCell,
createCell,
Fragment
- createCell,
- Fragment
} from 'web-cell';
-import { observer } from 'mobx-web-cell';
+import { observable } from 'mobx';
Expand All @@ -86,11 +85,14 @@ export interface MyTagProps extends WebCellProps {
})
@observer
-export class MyTag extends mixin<MyTagProps>() {
+export class MyTag extends WebCell<MyTagProps>() {
+export class MyTag extends HTMLElement {
+ declare props: MyTagProps;

@attribute
- @watch
+ @observable
count = 0;
- count = 0;
+ accessor count = 0;

- render({ count }: MyTagProps) {
+ render() {
Expand All @@ -109,15 +111,14 @@ export interface MyTagProps extends WebCellProps {
import {
component,
- mixin
+ WebCell
} from 'web-cell';

@component({
tagName: 'my-tag',
- renderTarget: 'children'
})
-export class MyTag extends mixin() {
+export class MyTag extends WebCell() {
+export class MyTag extends HTMLElement {
}
```

Expand All @@ -127,7 +128,6 @@ import {
import {
component,
- mixin
+ WebCell
} from 'web-cell';

@component({
Expand All @@ -136,7 +136,7 @@ import {
+ mode: 'open'
})
-export class MyTag extends mixin() {
+export class MyTag extends WebCell() {
+export class MyTag extends HTMLElement {
}
```

Expand All @@ -149,7 +149,6 @@ This makes **Shadow CSS** to react with the data of component instances.
import {
component,
- mixin
+ WebCell
} from 'web-cell';

@component({
Expand All @@ -163,7 +162,7 @@ import {
- }
})
-export class MyTag extends mixin() {
+export class MyTag extends WebCell() {
+export class MyTag extends HTMLElement {
render() {
return <>
+ <style>
Expand All @@ -183,14 +182,15 @@ import {

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

1. `mixin()` => `WebCell()`
1. `mixin()` => `HTMLElement` & its Sub-classes
2. `mixinForm()` => `WebField()`
3. `@watch` => `@observable`
3. `@watch` => `@observable accessor`

## Appendix: v3 prototype

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

[1]: https://github.com/mobxjs/mobx/blob/mobx4and5/docs/refguide/observer-component.md#local-observable-state-in-class-based-components
[2]: https://blog.cloudboost.io/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e
Expand Down
97 changes: 63 additions & 34 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

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

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

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

## Feature

### Engines comparison

| feature | WebCell 3 | WebCell 2 | React | Vue |
| :-----------: | :------------------: | :------------------: | :---------------------------: | :---------------------------------: |
| JS language | TypeScript 5 | TypeScript 4 | ECMAScript or TypeScript | ECMAScript or TypeScript |
| JS syntax | ES decorator stage-3 | ES decorator stage-2 | | |
| XML syntax | JSX import | JSX factory | JSX factory/import | HTML/Vue template or JSX (optional) |
| DOM API | Web components | Web components | HTML 5+ | HTML 5+ |
| view renderer | DOM renderer 2 | SnabbDOM | (built-in) | SnabbDOM (forked) |
| state API | MobX `@observable` | `this.state` | `this.state` or `useState()` | `this.$data` or `ref()` |
| props API | MobX `@observable` | `@watch` | `this.props` or `props => {}` | `this.$props` or `defineProps()` |
| state manager | MobX 6+ | MobX 4/5 | Redux | VueX |
| page router | JSX tags | JSX tags + JSON data | JSX tags | JSON data |
| asset bundler | Parcel 2 | Parcel 1 | webpack | Vite |

## Usage

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

### Project bootstrap

Command
#### Command

```shell
npm init -y
npm install web-cell
npm install parcel -D
npm install dom-renderer mobx web-cell
npm install parcel @parcel/config-default "@parcel/transformer-typescript-tsc" -D
```

`package.json`
#### `package.json`

```json
{
Expand All @@ -42,18 +59,37 @@ npm install parcel -D
}
```

[`tsconfig.json`](https://github.com/EasyWebApp/WebCell/blob/main/tsconfig.json)
#### `tsconfig.json`

```json
{
"compilerOptions": {
"target": "ES6",
"moduleResolution": "Node",
"useDefineForClassFields": true,
"jsx": "react-jsx",
"jsxImportSource": "dom-renderer"
}
}
```

`source/index.html`
#### `.parcelrc`

```json
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
```

#### `source/index.html`

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

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

```jsx
import { WebCellProps, WebCell, createCell, component } from 'web-cell';
import { WebCellProps, component } from 'web-cell';

export function InlineTag({ defaultSlot }: WebCellProps) {
return <span>{defaultSlot}</span>;
export function InlineTag({ children }: WebCellProps) {
return <span>{children}</span>;
}

@component({
tagName: 'sub-tag'
})
export class SubTag extends WebCell() {
export class SubTag extends HTMLElement {
render() {
return <InlineTag>test</InlineTag>;
}
Expand All @@ -87,17 +123,8 @@ export class SubTag extends WebCell() {

`source/TestTag.tsx`

```typescript
import {
WebCellProps,
WebCell,
createCell,
component,
attribute,
observer,
on,
Fragment
} from 'web-cell';
```tsx
import { WebCellProps, component, attribute, observer, on } from 'web-cell';
import { observable } from 'mobx';

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

class State {
@observable
status = '';
accessor status = '';
}

@component({
tagName: 'test-tag',
mode: 'open'
})
@observer
export class TestTag extends WebCell<TestTagProps>() {
export class TestTag extends HTMLElement {
declare props: TestTagProps;

@attribute
@observable
topic = 'Test';
accessor topic = 'Test';

state = new State();

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

## Life Cycle hooks

Expand Down Expand Up @@ -233,7 +262,7 @@ We recommend these libraries to use with WebCell:
[1]: https://www.webcomponents.org/
[2]: https://facebook.github.io/jsx/
[3]: https://www.typescriptlang.org
[4]: https://david-dm.org/EasyWebApp/WebCell
[4]: https://libraries.io/npm/web-cell
[5]: https://github.com/EasyWebApp/WebCell/actions/workflows/main.yml
[6]: https://github.com/996icu/996.ICU/blob/master/LICENSE
[7]: https://github.com/jaywcjlove/awesome-uikit
Expand Down
4 changes: 2 additions & 2 deletions source/Async.tsx → legacy/Async.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { observable } from 'mobx';

import { ComponentTag, WebCellProps, FunctionComponent } from './utility';
import { WebCellClass, WebCell } from './WebCell';
import { component, observer, reaction } from './decorator';
import { WebCellClass, WebCell } from '../source/WebCell';
import { component, observer, reaction } from '../source/decorator';
import { createCell } from './renderer';

export interface AsyncBoxProps extends WebCellProps {
Expand Down
Loading

0 comments on commit 6b14dfd

Please sign in to comment.