lithent 1.20.1 → 1.21.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.
Files changed (65) hide show
  1. package/README.md +82 -287
  2. package/dist/hook/useRenew.d.ts +6 -0
  3. package/dist/index.d.ts +4 -3
  4. package/dist/lithent.mjs +363 -308
  5. package/dist/lithent.mjs.map +1 -1
  6. package/dist/lithent.umd.js +1 -1
  7. package/dist/lithent.umd.js.map +1 -1
  8. package/dist/render.d.ts +1 -1
  9. package/dist/types/index.d.ts +9 -10
  10. package/dist/utils/index.d.ts +1 -0
  11. package/dist/utils/predicator.d.ts +1 -0
  12. package/dist/utils/universalRef.d.ts +1 -0
  13. package/dist/wDom.d.ts +6 -3
  14. package/examples/dist/assets/index-C8ubEFcX.js +2 -0
  15. package/examples/dist/assets/index-C8ubEFcX.js.map +1 -0
  16. package/examples/dist/index.html +1 -1
  17. package/ftags/dist/index.d.ts +7 -3
  18. package/ftags/dist/lithentFTags.mjs +19 -12
  19. package/ftags/dist/lithentFTags.mjs.map +1 -1
  20. package/ftags/dist/lithentFTags.umd.js +1 -1
  21. package/ftags/dist/lithentFTags.umd.js.map +1 -1
  22. package/ftags/src/index.ts +26 -1
  23. package/ftags/src/tests/ftags.ts +36 -1
  24. package/helper/dist/hook/context.d.ts +8 -1
  25. package/helper/dist/hook/lcontext.d.ts +18 -0
  26. package/helper/dist/hook/lstate.d.ts +5 -0
  27. package/helper/dist/hook/lstore.d.ts +28 -0
  28. package/helper/dist/index.d.ts +4 -0
  29. package/helper/dist/lithentHelper.mjs +297 -129
  30. package/helper/dist/lithentHelper.mjs.map +1 -1
  31. package/helper/dist/lithentHelper.umd.js +1 -1
  32. package/helper/dist/lithentHelper.umd.js.map +1 -1
  33. package/helper/src/hook/context.tsx +4 -4
  34. package/helper/src/hook/lcontext.tsx +197 -0
  35. package/helper/src/hook/lstate.ts +28 -0
  36. package/helper/src/hook/lstore.ts +243 -0
  37. package/helper/src/index.ts +8 -0
  38. package/helper/src/tests/lcontext.tsx +316 -0
  39. package/helper/src/tests/lstore.tsx +198 -0
  40. package/helper/src/tests/state.tsx +88 -0
  41. package/jsx-runtime/dist/index.d.ts +0 -1
  42. package/jsx-runtime/package.json +1 -1
  43. package/package.json +6 -3
  44. package/src/diff.ts +24 -13
  45. package/src/hook/internal/useUpdate.ts +2 -2
  46. package/src/hook/mountCallback.ts +5 -3
  47. package/src/hook/useRenew.ts +17 -0
  48. package/src/index.ts +4 -1
  49. package/src/render.ts +67 -68
  50. package/src/tests/core-component-remount.test.tsx +35 -0
  51. package/src/tests/core-composedRenew.test.tsx +62 -0
  52. package/src/tests/core-functional-vdom.test.tsx +47 -0
  53. package/src/tests/core-lmount.tsx +101 -0
  54. package/src/tests/core-loopDomReuseWithoutKey.tsx +68 -0
  55. package/src/tests/loopWithoutKey.tsx +49 -0
  56. package/src/types/index.ts +17 -12
  57. package/src/utils/index.ts +5 -1
  58. package/src/utils/predicator.ts +10 -7
  59. package/src/utils/redraw.ts +3 -2
  60. package/src/utils/universalRef.ts +1 -0
  61. package/src/wDom.ts +84 -20
  62. package/ssr/package.json +1 -1
  63. package/tag/dist/index.d.ts +0 -2
  64. package/examples/dist/assets/index-Ch3C2eh3.js +0 -2
  65. package/examples/dist/assets/index-Ch3C2eh3.js.map +0 -1
package/README.md CHANGED
@@ -1,342 +1,137 @@
1
+ <img src="./lithentDocs/public/lithent.png" alt="Lithent" height="60" align="left" style="margin-right: 10px;" />
1
2
 
2
- ## ➿ lithent
3
- > An extensible JSX-based virtual DOM library for lightweight use in a variety of environments.
3
+ # Lithent &nbsp; [![npm version](https://img.shields.io/npm/v/lithent.svg)](https://www.npmjs.com/package/lithent) [![Bundle Size](https://img.shields.io/bundlephobia/minzip/lithent)](https://bundlephobia.com/package/lithent) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
4
4
 
5
+ <br clear="left"/>
5
6
 
6
- ![lithent-text](https://github.com/user-attachments/assets/a8848484-d315-4f81-be3b-61490d2be2b9)
7
+ **Lithent is a JavaScript library for building lightweight, predictable UIs with familiar closure-based patterns.**
7
8
 
8
- ![npm](https://img.shields.io/npm/v/lithent)
9
- ![npm bundle size](https://img.shields.io/bundlephobia/minzip/lithent)
10
- ![npm](https://img.shields.io/npm/dt/lithent)
11
- ![NPM](https://img.shields.io/npm/l/lithent)
9
+ It trims away unnecessary magic and complex APIs so your UI code stays simple, explicit, and easy to reason about.
12
10
 
11
+ <br />
13
12
 
13
+ <div align="center">
14
14
 
15
+ ### 🚀 [Get Started](https://superlucky84.github.io/lithent/#/guide/quick-start) · 📖 [Documentation](https://superlucky84.github.io/lithent/#/) · 💡 [Examples](https://superlucky84.github.io/lithent/#/examples/1)
15
16
 
16
- - [Manual](https://medium.com/p/13d6fe6d3330)
17
- - [Korean Manual](https://medium.com/@superlucky84/lithent-전체-기능-설명서-3cfab04c0722)
18
- - [Home Page](https://superlucky84.github.io/lithent) - (This page is written in lithent.)
17
+ </div>
19
18
 
20
- Lithent were developed to make it easy to insert Virtual DOM component
21
- fragments into pages already drawn with SSR, and are intended to be
22
- used lightly in a variety of situations.
19
+ <br />
23
20
 
24
- `(lithent.mjs 14.25 kB │ gzip: 4.49 kB │ map: 59.74 kB)`
25
- `(lithent.umd.js 10.39 kB │ gzip: 4.03 kB │ map: 58.16 kB)`
21
+ ## Why Lithent?
26
22
 
27
- ## 🚩 Table of Contents
28
- - [Thanks for the introduction](#thanks-for-the-introduction)
29
- - [Why Use Lithent](#why-use-lithent)
30
- - [Basic Guide](#basic-guide)
31
- - [Lithent Ssr Boilerplate](#lithent-ssr-boilerplate)
32
- - [How To Install](#how-to-install)
33
- - [Use NPM](#use-npm)
34
- - [Use CDN](#or-use-cdn)
35
- - [With JSX](#with-jsx)
36
- - [With FTAGS](#with-ftags)
37
- - [With HTM](#with-htm)
38
- - [Examples](#examples)
39
- - [With ESM](#with-esm)
40
- - [With UMD](#with-umd)
41
- - [Context API](#context-api)
42
- - [Multi-value Provider Example](#multi-value-provider-example)
43
- - [Related Projects](#related-projects)
44
- - [Develop Guide](#develop-guide)
45
- - [Test](#test)
23
+ **Lightweight DOM manipulation without the framework weight.** The 4KB core drives complete UIs. Need state management? Opt into helpers like expansion packs instead of adopting a full stack.
46
24
 
47
- ## Thanks for the introduction
48
- - [unsuckjs](https://unsuckjs.com/)
49
- - [jster](https://jster.net/blog/jster-198/)
50
- - [webtoolsweekly](https://webtoolsweekly.com/archives/issue-531/)
25
+ Bring in only what you need — let the stack scale with your project.
51
26
 
52
- ## Why use Lithent?
27
+ **Design philosophy:**
28
+ - **Small Bundle** — 4KB core with optional extensions
29
+ - **Closure-based State** — No magic, just JavaScript
30
+ - **Manual or Reactive** — Choose your update strategy
31
+ - **Progressive Enhancement** — From static HTML to full SPA
53
32
 
54
- ### Lithent have the bare minimum of necessary functionality, with no unnecessary features.
33
+ <br />
55
34
 
56
- In a real-world development environment, you may want to use Virtual DOM lightly, with only the bare minimum of core functionality.
35
+ ## Quick start
57
36
 
58
- "Lithent" makes it easy to add, remove, and update virtual DOM to specific parts of a pre-drawn SSR page.
59
-
60
- "Lithent" has implemented the bare minimum functionality needed to create and update virtual DOM in general (we only need to know 'render', 'mounter', 'updater', 'renewer', 'mountCallback', and 'updateCallback').
61
-
62
- We provide code in the form of 'helpers' that extend the basic functionality, but using the helpers is only optional and users can extend and develop custom helpers for their own projects.
63
-
64
- ### Approach with the developer-friendly concept of closures between "component mounter" and "renderer"
65
-
66
- JavaScript users are used to using closures and love to develop with them.
67
-
68
- "Lithent" provides a familiar approach to my JavaScript development, as it leverages the properties of higher-order functions and closures to define and recycle the state of a component.
69
-
70
- ## Basic Guide
71
-
72
- - https://superlucky84.github.io/lithent/
73
-
74
- ## Lithent Ssr Boilerplate
75
-
76
- `Lithent-Ssr-Boilerplate` is a boilerplate designed for building server-side rendering (SSR) websites using the [Lithent](https://superlucky84.github.io/lithent/) UI library.
77
-
78
- * [README](https://github.com/superlucky84/lithent/tree/master/createLithent#readme)
37
+ ### Create a new project
79
38
 
80
39
  ```bash
81
- npx create-lithent-ssr@latest
40
+ npx create-lithent@latest
82
41
  ```
83
42
 
43
+ Pick a project name and template (SSR/SPA) and you’re ready to go.
84
44
 
85
- ## How To Install
86
-
87
- #### Use NPM
45
+ ### Install via npm
88
46
 
89
47
  ```bash
90
- pnpm add lithent
48
+ npm install lithent
91
49
  ```
92
50
 
93
- #### Or Use CDN
94
-
95
- * UMD : https://cdn.jsdelivr.net/npm/lithent@1.20.1/dist/lithent.umd.js
96
- * UMD-HELPER: https://cdn.jsdelivr.net/npm/lithent@1.20.1/helper/dist/lithentHelper.umd.js
97
- * UMD-FTAGS: https://cdn.jsdelivr.net/npm/lithent@1.20.1/ftags/dist/lithentFTags.umd.js
98
- * UMD-TAG: https://cdn.jsdelivr.net/npm/lithent@1.20.1/tag/dist/lithentTag.umd.js
99
- * ESM : https://cdn.jsdelivr.net/npm/lithent@1.20.1/dist/lithent.mjs
100
- * ESM-HELPER: https://cdn.jsdelivr.net/npm/lithent@1.20.1/helper/dist/lithentHelper.mjs
101
- * ESM-FTAGS: https://cdn.jsdelivr.net/npm/lithent@1.20.1/ftags/dist/lithentFTags.mjs
102
- * ESM-TAG: https://cdn.jsdelivr.net/npm/lithent@1.20.1/tag/dist/lithentTag.mjs
103
-
104
-
105
- It's easier to use lithent with JSX or HTM.
106
-
107
- #### With JSX
108
-
109
- * Lithent is built on top of JSX.
110
- * [Setting Guide](https://superlucky84.github.io/lithent/#install)
51
+ ### Use directly from a CDN
111
52
 
112
-
113
- #### With FTAGS
114
-
115
- You don't need to use h functions or JSX, you can call functions to mark them up.
116
- When creating a component, use `fMount` instead of `mount`.
117
- When creating a fragment, use `fFragment` instead of `Fragment`.
118
-
119
- ```ts
120
- /* ESM */
121
- import { render, h } from 'lithent';
122
- import { fTags, fFragment, fMount } from 'lithent/ftags';
123
-
124
- const { section, div, p, br, strong } = fTags;
125
-
126
- /* UMD
127
- <script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/dist/lithent.umd.js"></script>
128
- <script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/helper/dist/lithentHelper.umd.js"></script>
129
- <script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/ftags/dist/lithentFTags.umd.js"></script>
130
-
131
- const { render } = lithent;
132
- const { fTags, fMount, fFragment } = lithentFTags;
133
- const { section, div, p, br, strong } = fTags;
134
- */
135
-
136
- const fTagComponent = fMount<{ firstProp: number }>((_r, props, children) => {
137
- return () =>
138
- fFragment(
139
- 'first inner',
140
- div({ style: { border: '1px solid red' } }, 'second inner'),
141
- div('The props argument can be omitted.'),
142
- props.firstProp,
143
- ...children
144
- );
145
- });
146
-
147
- render(
148
- fTagComponent(
149
- { firstProp: 3 }, // The props argument can be omitted.
150
- div({ style: { border: '1px solid green' } }, `Fchildren1`),
151
- 'Fchildren2',
152
- br()
153
- ),
154
- document.getElementById('root')
155
- );
156
- ```
157
-
158
- #### With HTM
159
-
160
- - [Htm Github](https://github.com/developit/htm)
161
-
162
- ```js
163
- import { lTag } from 'lithent/tag';
164
- const destroy = render(lTag`<${Component} />`, document.getElementById('root'), document.getElementById('#insert-before-this-element'));
53
+ ```html
54
+ <script src="https://cdn.jsdelivr.net/npm/lithent/dist/lithent.umd.js"></script>
55
+ <script src="https://cdn.jsdelivr.net/npm/lithent/ftags/dist/lithentFTags.umd.js"></script>
165
56
  ```
166
57
 
58
+ > **[📦 View all available CDN URLs](https://superlucky84.github.io/lithent/#/guide/quick-start)**
167
59
 
168
- ## Examples
169
-
170
- - [More Examples](https://superlucky84.github.io/lithent/#examples)
60
+ <br />
171
61
 
172
- #### With ESM
173
- ```js
174
- import { h, render, mount, Fragment } from 'lithent';
175
- import { lTag } from 'lithent/tag';
176
- import { state } from 'lithent/helper';
62
+ ## Two ways to build components
177
63
 
178
- const Component = mount((renew, _props) => {
179
- const count = state(0, renew);
64
+ Lithent offers two complementary styles you can freely mix in the same project.
180
65
 
181
- const change = () => {
182
- count.value += 1;
183
- };
184
-
185
- // Updater
186
- return () => lTag`
187
- <${Fragment}>
188
- <li>count: ${count.value}</li>
189
- <button onClick=${change}>increase</button>
190
- <//>
191
- `;
192
- });
193
-
194
- // appendChild or insertBefore
195
- // The third argument is an optional value for insertBefore.
196
- const destroy = render(lTag`<${Component} />`, document.getElementById('root'), document.getElementById('#insert-before-this-element'));
197
- ```
198
-
199
- #### With UMD
66
+ **Manual Mode** — Explicit control with `renew()`
67
+ ```tsx
68
+ import { mount } from 'lithent';
200
69
 
201
- ```html
202
- <script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/dist/lithent.umd.js"></script>
203
- <!--script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/helper/dist/lithentHelper.umd.js"></script-->
204
- <script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/tag/dist/lithentTag.umd.js"></script>
205
- <!--script src="https://cdn.jsdelivr.net/npm/lithent@1.20.1/ftags/dist/lithentFTags.umd.js"></script-->
206
-
207
- <div id="root"></div>
208
-
209
- <script>
210
- const { h, Fragment, render, mount } = lithent;
211
- const { lTag } = lithentTag;
212
- // const { state } = lithentHelper;
213
-
214
- const Component = mount(renew => {
215
- count = 0;
216
-
217
- const change = () => {
218
- count += 1;
219
- renew();
220
- };
221
-
222
- // Updater
223
- return () => lTag`
224
- <${Fragment}>
225
- <li>count: ${count}</li>
226
- <button onClick=${change}>increase</button>
227
- <//>
228
- `;
70
+ const Counter = mount(renew => {
71
+ let count = 0;
72
+ return () => <button onClick={() => { count++; renew(); }}>{count}</button>;
229
73
  });
230
-
231
- // appendChild or insertBefore
232
- // The third argument is an optional value for insertBefore.
233
- const destroy = render(lTag`<${Component} />`, document.getElementById('root'), document.getElementById('#insert-before-this-element'));
234
- </script>
235
74
  ```
236
75
 
237
- ## Context API
238
-
239
- Lithent-helper ships a lightweight context system so you can share state without manual prop drilling. Providers stay isolated from consumer renews and you can subscribe to every key or only the slices you need.
240
-
241
- ### Multi-value Provider Example
242
-
76
+ **Light API Mode** — Automatic reactivity
243
77
  ```tsx
244
- import { h, mount } from 'lithent';
245
- import { createContext } from 'lithent-helper';
246
-
247
- type AppState = { user: string; theme: string; count: number };
248
-
249
- const AppContext = createContext<AppState>();
250
- const { Provider, contextState, useContext } = AppContext;
251
-
252
- const AppProvider = mount((_renew, _props, children) => {
253
- // Provider owns the shared state; consumers cannot trigger its renew directly.
254
- const user = contextState('Alice');
255
- const theme = contextState('dark');
256
- const count = contextState(0);
257
-
258
- return () => (
259
- <Provider user={user} theme={theme} count={count}>
260
- {children}
261
- </Provider>
262
- );
263
- });
78
+ import { lmount } from 'lithent';
79
+ import { lstate } from 'lithent/helper';
264
80
 
265
- const StatsPanel = mount(renew => {
266
- // Omitting the subscribe list subscribes to every value exposed by the Provider.
267
- const ctx = useContext(AppContext, renew);
268
- return () => (
269
- <section>
270
- <p>User: {ctx.user?.value}</p>
271
- <p>Theme: {ctx.theme?.value}</p>
272
- <p>Count: {ctx.count?.value}</p>
273
- <button onClick={() => ctx.count && (ctx.count.value += 1)}>
274
- Increment
275
- </button>
276
- <button onClick={() => ctx.count && (ctx.count.value = 3)}>
277
- Set count to 3
278
- </button>
279
- </section>
280
- );
281
- });
282
-
283
- const ThemeBadge = mount(renew => {
284
- // Subscribe only to the theme key; user/count updates will not rerender this component.
285
- const ctx = useContext(AppContext, renew, ['theme']);
286
- return () => <span>Theme: {ctx.theme?.value}</span>;
287
- });
288
-
289
- const App = mount(() => {
290
- return () => (
291
- <AppProvider>
292
- <StatsPanel />
293
- <ThemeBadge />
294
- </AppProvider>
295
- );
81
+ const Counter = lmount(() => {
82
+ const count = lstate(0);
83
+ return () => <button onClick={() => count.value++}>{count.value}</button>;
296
84
  });
297
85
  ```
298
86
 
299
- ## Related Projects
300
- - [htm](https://www.npmjs.com/package/htm) - making **H**yperscript **T**agged **M**arkup possible
87
+ > **[📚 Explore component patterns in detail](https://superlucky84.github.io/lithent/#/guide/mounter)**
301
88
 
302
- ## Develop Guide
89
+ <br />
303
90
 
304
- It's open source, so you can develop and contribute together.
91
+ ## Key features
305
92
 
306
- ### pnpm install (this project was created using pnpm.)
93
+ ### Core
94
+ - **mount / lmount** — Component initialization
95
+ - **Portal** — Render outside parent DOM
96
+ - **Hooks** — Lifecycle callbacks (`mountCallback`, `updateCallback`, `mountReadyCallback`)
97
+ - **Ref** — Direct DOM access
307
98
 
308
- ```bash
309
- npm install -g pnpm
310
- ```
99
+ ### Helpers (optional)
100
+ - **state / lstate** — Reactive state management
101
+ - **computed** — Derived values
102
+ - **effect** — Side effects
103
+ - **store / lstore** — Global state
104
+ - **context / lcontext** — Cross-component data sharing
311
105
 
312
- ### project install
106
+ ### Template options
107
+ - **JSX** — Via Vite plugin
108
+ - **FTags** — Function-style tags (no build step)
109
+ - **HTM** — Tagged template literals
110
+ - **Template Strings** — Custom templates
313
111
 
314
- ```bash
315
- git clone https://github.com/superlucky84/lithent.git
112
+ > **[📖 View full API reference](https://superlucky84.github.io/lithent/#/)**
316
113
 
317
- cd lithent
114
+ <br />
318
115
 
319
- pnpm install
320
- ```
116
+ ## Ecosystem
321
117
 
322
- ### project build
118
+ | Package | Description |
119
+ |--------|-------------|
120
+ | [lithent](https://www.npmjs.com/package/lithent) | Core library (~4KB) |
121
+ | [lithent/helper](https://www.npmjs.com/package/lithent) | Reactive state helpers |
122
+ | [lithent/ssr](https://www.npmjs.com/package/lithent) | Server‑side rendering |
123
+ | [lithent/ftags](https://www.npmjs.com/package/lithent) | Function‑style tag API |
124
+ | [lithent/tag](https://www.npmjs.com/package/lithent) | HTM template support |
125
+ | [create-lithent](https://www.npmjs.com/package/create-lithent) | Project scaffolding tool |
323
126
 
324
- ```bash
325
- pnpm build
326
- ```
127
+ <br />
327
128
 
328
- ### Running the development environment
129
+ ## License
329
130
 
330
- ```bash
331
- pnpm dev // or pnpm dev:core
332
- ```
131
+ [MIT](LICENSE) © [superlucky84](https://github.com/superlucky84)
333
132
 
334
- ## Test
133
+ <br />
335
134
 
336
- > To fully test everything, including plugins like helper and ftags, a build is required.
337
-
338
- ```bash
339
- pnpm install
340
- pnpm build
341
- pnpm test
342
- ```
135
+ <div align="center">
136
+ <sub>Built with ❤️ by the Lithent community</sub>
137
+ </div>
@@ -0,0 +1,6 @@
1
+ import { Renew } from '../types';
2
+ /**
3
+ * Hook to get the renew (component update) function.
4
+ * Useful for lmount components that need to trigger updates.
5
+ */
6
+ export declare const useRenew: () => Renew;
package/dist/index.d.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import type { Props } from './types';
2
- export { h, Fragment, portal, mount, replaceWDom } from './wDom';
2
+ export { h, Fragment, portal, mount, lmount, replaceWDom } from './wDom';
3
3
  export { isPropType } from './utils';
4
4
  export { componentUpdate } from './utils/redraw';
5
- export { getComponentKey, componentMap, getComponentSubInfo, setComponentMapManualMode, disposeComponentEntry, } from './utils/universalRef';
5
+ export { getComponentKey, componentMap, getComponentSubInfo, setComponentMapManualMode, disposeComponentEntry, lmountComponentSet, } from './utils/universalRef';
6
6
  export { render } from './render';
7
7
  export { mountCallback } from './hook/mountCallback';
8
8
  export { mountReadyCallback } from './hook/mountReadyCallback';
9
9
  export { updateCallback } from './hook/updateCallback';
10
10
  export { ref, nextTick } from './hook/ref';
11
- export type { WDom, TagFunction, TagFunctionResolver, FragmentFunction, Component, ComponentSubKey, ComponentMap, ComponentInfo, CompKey, Props, Renew, MiddleStateWDomChildren, MiddleStateWDom, NodePointer, Param, } from './types';
11
+ export { useRenew } from './hook/useRenew';
12
+ export type { WDom, TagFunction, TagFunctionResolver, FragmentFunction, Component, LComponent, ComponentSubKey, ComponentMap, ComponentInfo, CompKey, Props, Renew, MiddleStateWDomChildren, MiddleStateWDom, NodePointer, Param, } from './types';
12
13
  declare global {
13
14
  namespace JSX {
14
15
  interface IntrinsicElements {