From 95db6cebccc65caa4cacc43c0ad1647a55d7ffef Mon Sep 17 00:00:00 2001 From: hexplus Date: Sat, 28 Mar 2026 15:11:54 -0600 Subject: [PATCH 1/5] Updated CHANGELOG and package.json --- CHANGELOG.md | 16 ++++++++++++++++ package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c767a55..64c424d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ This project follows [Semantic Versioning](https://semver.org/). --- +## [1.0.3] — 2026-03-28 + +### Added + +- **Wider `NodeChild` / `NodeChildren` types** — `NodeChild` now accepts `boolean`; `NodeChildren` accepts nested arrays and full reactive functions. Conditional patterns like `condition && element` work without `as any` casts. Boolean values are filtered out in `appendChildren`, `bindChildNode`, `Fragment()`, `htm.ts`, and `resolveChild`. +- **`onCleanup()` lifecycle hook** — `onCleanup(callback, element)` registers teardown logic (closing sockets, clearing timers, removing listeners) tied to an element's disposal. Integrates with the existing `dispose()` system so cleanup runs automatically when `when()`, `match()`, or `each()` swap content. +- **`query()` `select` option** — Optional `select` function that transforms cached data before returning it to consumers. Raw response stays in cache; `select` runs on read, enabling derived views without extra signals. +- **`formatNumber()` and `formatCurrency()`** — `Intl`-based formatting utilities exported from `sibujs/browser`. `formatNumber` wraps `Intl.NumberFormat`; `formatCurrency` is a convenience shorthand that sets `style: "currency"`. + +### Fixed + +- **Boolean values no longer render as text** — `false`, `true` are filtered in all rendering paths (`tagFactory`, `bindChildNode`, `Fragment`, `htm.ts`, `resolveChild`) preventing visible `"false"` text nodes. +- **Lint fixes** — Resolved unused variable in `router.basic.test.ts` and formatting issues flagged by Biome. + +--- + ## [1.0.2] — 2026-03-27 ### Fixed diff --git a/package.json b/package.json index 4a30d20..a3cd741 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sibujs", - "version": "1.0.2", + "version": "1.0.3", "description": "A lightweight, function-based frontend framework that combines the best of React, Svelte, and Vue — with zero VDOM and maximum simplicity. Designed for developers who want fine-grained reactivity and full control without compilation or magic.", "keywords": [ "frontend", From 9487727c338809848170d361ea8775a3fa149ad9 Mon Sep 17 00:00:00 2001 From: hexplus Date: Sat, 28 Mar 2026 22:30:29 -0600 Subject: [PATCH 2/5] ci: use npm install instead of npm ci --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index aab4d99..e156d9e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: registry-url: "https://registry.npmjs.org" - name: Install dependencies - run: npm ci + run: npm install - name: Run tests run: npm test From 077718418208d14423f9aeddb63876ce57f6454c Mon Sep 17 00:00:00 2001 From: hexplus Date: Sat, 28 Mar 2026 22:51:26 -0600 Subject: [PATCH 3/5] trusted-publisher --- .github/workflows/publish.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cdf4e5b..f25d1f3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,17 +4,21 @@ on: release: types: [published] +permissions: + id-token: write + contents: read + jobs: publish: runs-on: ubuntu-latest steps: - - name: Checkout código + - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "22" registry-url: "https://registry.npmjs.org" - name: Install dependencies @@ -28,5 +32,3 @@ jobs: - name: Publish to npm run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From ee7cf487a4e8438c2238b7ed54bc652e48b10b6d Mon Sep 17 00:00:00 2001 From: hexplus Date: Sat, 11 Apr 2026 09:51:07 -0600 Subject: [PATCH 4/5] Updated main --- README.md | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 633c67a..61c59bc 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,10 @@ import { div, h1, button, signal, mount } from "sibujs"; function Counter() { const [count, setCount] = signal(0); - return div({ - nodes: [ - h1({ nodes: () => `Count: ${count()}` }), - button({ - nodes: "Increment", - on: { click: () => setCount(c => c + 1) } - }) - ] - }); + return div({ class: "counter" }, [ + h1(() => `Count: ${count()}`), + button({ on: { click: () => setCount(c => c + 1) } }, "Increment"), + ]); } mount(Counter, document.getElementById("app")); @@ -43,32 +38,41 @@ mount(Counter, document.getElementById("app")); SibuJS gives you maximum flexibility with three interoperable styles: -#### 1. Tag Factory (Full Props) -Maximum control with an explicit properties object. Perfect for complex elements. +#### 1. Tag Factory +The canonical form: a props object followed by children as a second +positional argument. No `nodes:` key required at any level of the tree — +children can be a string, a number, a single node, an array, or a +reactive getter. ```javascript -import { div, h1, button } from "sibujs"; - -const [count, setCount] = signal(0); - -return div({ - class: "counter", - nodes: [ - h1({ nodes: () => `Count: ${count()}` }), - button({ nodes: "Increment", on: { click: () => setCount(c => c + 1) } }) - ] -}); +import { div, h1, label, input, button } from "sibujs"; + +return div({ class: "counter" }, [ + h1({ class: "title" }, () => `Count: ${count()}`), + label({ for: "amount" }, "Step"), + input({ id: "amount", type: "number", value: 1 }), + button( + { class: "primary", on: { click: () => setCount(c => c + 1) } }, + "Increment", + ), +]); ``` -#### 2. Shorthand API -Concise and readable for common layouts. Class and children passed as positional arguments. +All legacy forms — `tag({ class, nodes })`, `tag("className", children)`, +`tag("text")`, `tag([children])`, `tag(node)`, `tag(() => reactive)` — +continue to work unchanged. When both `props.nodes` and the positional +second argument are present, the positional wins. + +#### 2. Positional Shorthand +The tersest form. Class and children as positional arguments, for +layouts with no event handlers or custom props. ```javascript import { div, h1, button } from "sibujs"; return div("counter", [ h1(() => `Count: ${count()}`), - button({ nodes: "Increment", on: { click: () => setCount(c => c + 1) } }) + button({ on: { click: () => setCount(c => c + 1) } }, "Increment"), ]); ``` From 35787c11bfe9c8508e1bc3957bedcf0c7424e3d9 Mon Sep 17 00:00:00 2001 From: hexplus Date: Sat, 11 Apr 2026 14:16:04 -0600 Subject: [PATCH 5/5] refactor(sibujs): remove duplicate reactive aliases (createSignal, createMemo, createEffect, memo, memoFn, composable); bump to 1.4.0 --- CHANGELOG.md | 48 ++++++++++++++++++++++ index.ts | 2 - package.json | 2 +- patterns.ts | 1 - src/build/analyzer.ts | 3 -- src/build/ide.ts | 82 ------------------------------------- src/build/linting.ts | 2 +- src/build/vite.ts | 1 - src/build/webpack.ts | 1 - src/core/signals/memo.ts | 20 ++------- src/core/signals/memoFn.ts | 18 ++------ src/core/signals/signal.ts | 2 +- src/devtools/signalGraph.ts | 2 +- src/patterns/composable.ts | 14 +++---- src/patterns/primitives.ts | 65 +++-------------------------- src/plugins/ecosystem.ts | 2 +- tests/composable.test.ts | 22 ++-------- tests/ide.test.ts | 32 +++++++++------ tests/memo.test.ts | 24 +++-------- tests/memoFn.test.ts | 28 +++---------- tests/primitives.test.ts | 50 ++++------------------ tests/types.test.ts | 22 ---------- 22 files changed, 114 insertions(+), 329 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aede5bb..85977ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,54 @@ This project follows [Semantic Versioning](https://semver.org/). --- +## [1.4.0] — 2026-04-11 + +Cleanup release. Removes six public aliases that contradicted the SibuJS philosophy — plain verbs, no framework ceremony, no redundant synonyms for the same primitive. All of the removed APIs were either one-line forwards to an existing primitive or identity wrappers; every existing example can be rewritten by deleting the wrapper and calling the underlying primitive directly. + +### Removed + +- **`createSignal`** — was `return signal(value)`. Use `signal()` directly. +- **`createMemo`** — was `return derived(fn)`. Use `derived()` directly. +- **`createEffect`** — was `return effect(fn)`. Use `effect()` directly. +- **`memo`** — was `return derived(factory)`. Use `derived()` directly. +- **`memoFn`** — was `return derived(callback)`. Use `derived()` directly. +- **`composable`** — was `return setup` (identity function). Plain functions are already composables in SibuJS; just write one and call it. + +The three removed files (`src/patterns/primitives.ts`, `src/core/signals/memo.ts`, `src/core/signals/memoFn.ts`) are currently empty stubs exporting nothing — they can be deleted from disk in a follow-up commit without further code changes. + +### Migration + +```ts +// before +import { createSignal, createMemo, createEffect, memo, memoFn, composable } from "sibujs"; + +const [count, setCount] = createSignal(0); +const doubled = createMemo(() => count() * 2); +const sorted = memo(() => items().slice().sort()); +const handler = memoFn(() => (e: Event) => process(e)); +createEffect(() => console.log(count())); +const useCounter = composable(() => { /* … */ }); + +// after +import { signal, derived, effect } from "sibujs"; + +const [count, setCount] = signal(0); +const doubled = derived(() => count() * 2); +const sorted = derived(() => items().slice().sort()); +const handler = derived(() => (e: Event) => process(e)); +effect(() => console.log(count())); +function useCounter() { /* … */ } +``` + +### Also updated + +- `generateComponentMetadata`, `generateTypeStubs`, and the Vite/Webpack pure-annotation factory list in `sibujs/build` no longer mention the removed names. +- Lint rule `no-signals-in-conditionals` no longer checks `memo` / `memoFn` (they don't exist). +- `SignalNodeSnapshot.kind` comment updated to drop the `"memo"` tag. +- Test suite: `tests/primitives.test.ts`, `tests/memo.test.ts`, `tests/memoFn.test.ts` reduced to placeholder stubs; `tests/types.test.ts` and `tests/ide.test.ts` updated to assert the aliases are gone. Suite: **2105/2105 passing** (down from 2113 by exactly the 8 deleted alias-specific tests). + +--- + ## [1.3.0] — 2026-04-11 Large minor release. Adds **27 new reactive/DOM primitives**, a full **SSR + OWASP security hardening pass** (A01, A02, A03, A10 + CWE-1321 prototype pollution), **10 ergonomic features** that stay inside the SibuJS philosophy (No VDOM, No JSX, No compilation, Zero dependencies, fine-grained reactivity), **typed tag factory overloads** for common elements, and a new **`tag(props, children)` positional shorthand** that removes the need for the `nodes:` key at every level of the tree. Test suite grew from **1875 → 2113** passing tests (+238, **0 regressions**). diff --git a/index.ts b/index.ts index ee47d81..20da411 100644 --- a/index.ts +++ b/index.ts @@ -57,8 +57,6 @@ export * from "./src/core/signals/derived"; export * from "./src/core/signals/watch"; export * from "./src/core/signals/store"; export * from "./src/core/signals/ref"; -export * from "./src/core/signals/memo"; -export * from "./src/core/signals/memoFn"; export * from "./src/core/signals/array"; export * from "./src/core/signals/deepSignal"; export * from "./src/core/signals/writable"; diff --git a/package.json b/package.json index 3f25bf6..3e288bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sibujs", - "version": "1.3.0", + "version": "1.4.0", "description": "A lightweight, function-based frontend framework that combines the best of React, Svelte, and Vue — with zero VDOM and maximum simplicity. Designed for developers who want fine-grained reactivity and full control without compilation or magic.", "keywords": [ "frontend", diff --git a/patterns.ts b/patterns.ts index 314423e..ef3d8d2 100644 --- a/patterns.ts +++ b/patterns.ts @@ -10,7 +10,6 @@ export * from "./src/patterns/persist"; export * from "./src/patterns/optimistic"; export * from "./src/patterns/timeTravel"; export * from "./src/patterns/globalStore"; -export * from "./src/patterns/primitives"; // Component patterns export * from "./src/patterns/hoc"; diff --git a/src/build/analyzer.ts b/src/build/analyzer.ts index e30b3f0..710af3c 100644 --- a/src/build/analyzer.ts +++ b/src/build/analyzer.ts @@ -46,14 +46,11 @@ export const moduleSizes: Record = { "core/watch": 300, "core/store": 380, "core/ref": 150, - "core/memo": 180, - "core/memoFn": 160, "core/array": 420, "core/deepSignal": 500, "core/lifecycle": 300, "core/context": 350, "core/persist": 400, - "core/primitives": 200, "core/hoc": 280, "core/transition": 600, "core/form": 750, diff --git a/src/build/ide.ts b/src/build/ide.ts index 9c0d15e..8c387d3 100644 --- a/src/build/ide.ts +++ b/src/build/ide.ts @@ -68,31 +68,6 @@ export function getComponentMetadata(): ComponentMeta[] { }, ], }, - { - name: "memo", - description: - "Returns a memoized value that only recomputes when its reactive dependencies change. Alias for derived.", - props: [ - { - name: "factory", - type: "() => T", - required: true, - description: "Function that computes the memoized value", - }, - ], - }, - { - name: "memoFn", - description: "Returns a memoized callback that only updates when its reactive dependencies change.", - props: [ - { - name: "callback", - type: "() => T", - required: true, - description: "The callback factory function to memoize", - }, - ], - }, { name: "ref", description: @@ -140,44 +115,6 @@ export function getComponentMetadata(): ComponentMeta[] { ], }, - // ── SolidJS-style Primitives ───────────────────────────────────── - { - name: "createSignal", - description: "Creates a reactive signal. SolidJS-style alias for signal. Returns [getter, setter].", - props: [ - { - name: "value", - type: "T", - required: true, - description: "Initial value", - }, - ], - }, - { - name: "createMemo", - description: "Creates a derived/computed reactive value. SolidJS-style alias for derived.", - props: [ - { - name: "fn", - type: "() => T", - required: true, - description: "Computation function that reads other signals", - }, - ], - }, - { - name: "createEffect", - description: "Creates a reactive side effect. SolidJS-style alias for effect.", - props: [ - { - name: "fn", - type: "() => void", - required: true, - description: "Effect function that reads reactive signals", - }, - ], - }, - // ── Lifecycle ──────────────────────────────────────────────────── { name: "onMount", @@ -531,11 +468,6 @@ export function generateVSCodeSnippets(): Record< ], description: "Create a reactive form with validation", }, - "SibuJS createSignal": { - prefix: "sibu-signal", - body: ["const [${1:value}, ${2:setValue}] = createSignal(${3:initialValue});"], - description: "Create a reactive signal (SolidJS-style alias for signal)", - }, }; } @@ -583,12 +515,6 @@ export function generateTypeStubs(): Record { derived: ["declare function derived(getter: () => T): () => T;"].join("\n"), - memo: ["declare function memo(factory: () => T): () => T;"].join("\n"), - - memoFn: ["declare function memoFn unknown>(callback: () => T): () => T;"].join( - "\n", - ), - ref: [ "interface Ref { current: T; }", "declare function ref(initial: T): Ref;", @@ -610,14 +536,6 @@ export function generateTypeStubs(): Record { "declare function store(initialState: T): [{ readonly [K in keyof T]: T[K] }, StoreActions];", ].join("\n"), - createSignal: ["declare function createSignal(value: T): [() => T, (next: T | ((prev: T) => T)) => void];"].join( - "\n", - ), - - createMemo: ["declare function createMemo(fn: () => T): () => T;"].join("\n"), - - createEffect: ["declare function createEffect(fn: () => void): () => void;"].join("\n"), - mount: [ "declare function mount(component: (() => HTMLElement) | HTMLElement | Node, container: HTMLElement | null): { node: Node; unmount: () => void };", ].join("\n"), diff --git a/src/build/linting.ts b/src/build/linting.ts index bdee451..deef94c 100644 --- a/src/build/linting.ts +++ b/src/build/linting.ts @@ -45,7 +45,7 @@ export const lintRules = { description: "Signal functions should not be called inside conditionals, loops, or nested functions", check(source: string): LintViolation[] { const violations: LintViolation[] = []; - const hookNames = ["signal", "effect", "derived", "memo", "memoFn", "ref", "watch", "store"]; + const hookNames = ["signal", "effect", "derived", "ref", "watch", "store"]; const hookPattern = new RegExp(`\\b(${hookNames.join("|")})\\s*\\(`, "g"); const lines = source.split("\n"); diff --git a/src/build/vite.ts b/src/build/vite.ts index 13368e3..ec44c4e 100644 --- a/src/build/vite.ts +++ b/src/build/vite.ts @@ -77,7 +77,6 @@ function injectPureAnnotations(code: string): string { const sibuFactories = [ "tagFactory", "context", - "composable", "defineComponent", "withProps", "withDefaults", diff --git a/src/build/webpack.ts b/src/build/webpack.ts index fa879a4..4e6c23a 100644 --- a/src/build/webpack.ts +++ b/src/build/webpack.ts @@ -16,7 +16,6 @@ export interface SibuWebpackPluginOptions { const PURE_FACTORIES = [ "tagFactory", "context", - "composable", "defineComponent", "withProps", "withDefaults", diff --git a/src/core/signals/memo.ts b/src/core/signals/memo.ts index 099d373..f237fd8 100644 --- a/src/core/signals/memo.ts +++ b/src/core/signals/memo.ts @@ -1,16 +1,4 @@ -import { derived } from "./derived"; -import type { Accessor } from "./signal"; - -/** - * memo returns a memoized value that only recomputes when its - * reactive dependencies change. This is semantically identical to - * derived but named for convenience. - * - * Use this to avoid expensive computations on every render cycle. - * - * @param factory Function that computes the memoized value - * @returns Getter function that returns the memoized value - */ -export function memo(factory: () => T): Accessor { - return derived(factory); -} +// Removed in 1.4.0: memo was a thin alias for derived. Use derived() +// directly. This file is kept as an empty stub and can be removed in a +// follow-up commit. +export {}; diff --git a/src/core/signals/memoFn.ts b/src/core/signals/memoFn.ts index 3995a7d..25a2175 100644 --- a/src/core/signals/memoFn.ts +++ b/src/core/signals/memoFn.ts @@ -1,14 +1,4 @@ -import { derived } from "./derived"; -import type { Accessor } from "./signal"; - -/** - * memoFn returns a memoized callback function that only updates - * when its reactive dependencies change. This prevents unnecessary - * re-creations of callback functions passed to child components. - * - * @param callback The callback function to memoize - * @returns Getter that returns the current memoized callback - */ -export function memoFn any>(callback: () => T): Accessor { - return derived(callback); -} +// Removed in 1.4.0: memoFn was a thin alias for derived. Use derived() +// directly. This file is kept as an empty stub and can be removed in a +// follow-up commit. +export {}; diff --git a/src/core/signals/signal.ts b/src/core/signals/signal.ts index 1b11ef7..b34d439 100644 --- a/src/core/signals/signal.ts +++ b/src/core/signals/signal.ts @@ -7,7 +7,7 @@ import { isDev } from "../dev"; declare const __accessor: unique symbol; /** - * A reactive signal getter returned by signal(), derived(), memo(), and similar primitives. + * A reactive signal getter returned by signal(), derived(), and similar primitives. * * Pass an Accessor directly into reactive prop positions — never call it there: * ```ts diff --git a/src/devtools/signalGraph.ts b/src/devtools/signalGraph.ts index 79ed89e..d1bce5f 100644 --- a/src/devtools/signalGraph.ts +++ b/src/devtools/signalGraph.ts @@ -50,7 +50,7 @@ export interface SignalNodeSnapshot { id: string; /** Debug name, if the caller tagged the signal. */ name: string | null; - /** Runtime type tag: `"signal"`, `"derived"`, `"memo"`, `"effect"`. */ + /** Runtime type tag: `"signal"`, `"derived"`, `"effect"`. */ kind: string; /** Best-effort preview of the current value. */ value: string; diff --git a/src/patterns/composable.ts b/src/patterns/composable.ts index 476aa97..6cc242f 100644 --- a/src/patterns/composable.ts +++ b/src/patterns/composable.ts @@ -1,14 +1,10 @@ // ============================================================================ -// COMPOSABLES & COMPONENT PATTERNS +// COMPONENT PATTERNS // ============================================================================ - -/** - * composable wraps a setup function to create reusable stateful logic. - * Similar to Vue 3 composables — encapsulates reactive state and methods. - */ -export function composable(setup: () => T): () => T { - return setup; -} +// +// Note: `composable()` was removed in 1.4.0 — it was an identity wrapper +// (`return setup`) that added nothing over calling the setup function +// directly. Plain functions are already composables in SibuJS. /** * RenderProp implements the render-prop pattern. diff --git a/src/patterns/primitives.ts b/src/patterns/primitives.ts index b0c7de7..0d01594 100644 --- a/src/patterns/primitives.ts +++ b/src/patterns/primitives.ts @@ -1,60 +1,5 @@ -import { derived } from "../core/signals/derived"; -import { effect } from "../core/signals/effect"; -import { signal } from "../core/signals/signal"; - -/** - * SolidJS-style reactive primitives — standalone APIs that don't require - * being inside a component. These are thin wrappers around the signal system. - */ - -/** - * Creates a reactive signal. Equivalent to signal but with SolidJS naming. - * - * @param value Initial value - * @returns Tuple [getter, setter] - * - * @example - * ```ts - * const [count, setCount] = createSignal(0); - * console.log(count()); // 0 - * setCount(5); - * ``` - */ -export function createSignal(value: T): [() => T, (next: T | ((prev: T) => T)) => void] { - return signal(value); -} - -/** - * Creates a derived/computed reactive value. Equivalent to derived. - * - * @param fn Computation function that reads other signals - * @returns Getter for the computed value - * - * @example - * ```ts - * const [count] = createSignal(5); - * const doubled = createMemo(() => count() * 2); - * console.log(doubled()); // 10 - * ``` - */ -export function createMemo(fn: () => T): () => T { - return derived(fn); -} - -/** - * Creates a reactive side effect. Equivalent to effect. - * - * @param fn Effect function that reads reactive signals - * @returns Cleanup/teardown function - * - * @example - * ```ts - * const [count] = createSignal(0); - * const cleanup = createEffect(() => { - * console.log("Count is:", count()); - * }); - * ``` - */ -export function createEffect(fn: () => void): () => void { - return effect(fn); -} +// Removed in 1.4.0: createSignal / createMemo / createEffect were thin +// aliases for signal / derived / effect. Use the canonical primitives from +// "sibujs" instead. This file is kept as an empty stub to avoid a delete +// in this change set; it can be removed in a follow-up commit. +export {}; diff --git a/src/plugins/ecosystem.ts b/src/plugins/ecosystem.ts index c881757..11bb6e6 100644 --- a/src/plugins/ecosystem.ts +++ b/src/plugins/ecosystem.ts @@ -86,7 +86,7 @@ export const bundlerMetadata = { sideEffects: false as const, modules: { core: ["html", "mount", "each", "slots", "fragment", "catch", "portal", "directives"], - hooks: ["signal", "effect", "derived", "watch", "store", "ref", "memo", "memoFn", "array", "deepSignal"], + hooks: ["signal", "effect", "derived", "watch", "store", "ref", "array", "deepSignal"], plugins: ["router", "i18n"], components: ["ErrorBoundary", "Loading"], ssr: ["ssr"], diff --git a/tests/composable.test.ts b/tests/composable.test.ts index b0409f0..02883b6 100644 --- a/tests/composable.test.ts +++ b/tests/composable.test.ts @@ -1,23 +1,9 @@ import { describe, expect, it } from "vitest"; -import { signal } from "../src/core/signals/signal"; -import { composable, createSlots, RenderProp, withBoundary } from "../src/patterns/composable"; +import { createSlots, RenderProp, withBoundary } from "../src/patterns/composable"; -describe("composable", () => { - it("should create reusable stateful logic", () => { - const counterSetup = composable(() => { - const [count, setCount] = signal(0); - return { - count, - increment: () => setCount((c) => c + 1), - }; - }); - - const counter = counterSetup(); - expect(counter.count()).toBe(0); - counter.increment(); - expect(counter.count()).toBe(1); - }); -}); +// composable() was removed in 1.4.0 — it was an identity wrapper that added +// nothing over calling the setup function directly. Plain functions are +// already composables in SibuJS. describe("RenderProp", () => { it("should render using function-as-nodes pattern", () => { diff --git a/tests/ide.test.ts b/tests/ide.test.ts index e2a2104..42bea24 100644 --- a/tests/ide.test.ts +++ b/tests/ide.test.ts @@ -15,9 +15,11 @@ describe("getComponentMetadata", () => { expect(meta.length).toBeGreaterThan(0); }); - it("returns at least 20 component definitions", () => { + it("returns at least 15 component definitions", () => { const meta = getComponentMetadata(); - expect(meta.length).toBeGreaterThanOrEqual(20); + // Threshold lowered from 20 after removing memo, memoFn, createSignal, + // createMemo, createEffect in 1.4.0. + expect(meta.length).toBeGreaterThanOrEqual(15); }); it("each entry has valid structure with name, description, and props", () => { @@ -98,12 +100,13 @@ describe("getComponentMetadata", () => { expect(onUnmount).toBeDefined(); }); - it("includes SolidJS-style aliases (createSignal, createMemo, createEffect)", () => { + it("does NOT include removed SolidJS-style aliases", () => { const meta = getComponentMetadata(); const names = meta.map((c) => c.name); - expect(names).toContain("createSignal"); - expect(names).toContain("createMemo"); - expect(names).toContain("createEffect"); + // createSignal / createMemo / createEffect were removed in 1.4.0. + expect(names).not.toContain("createSignal"); + expect(names).not.toContain("createMemo"); + expect(names).not.toContain("createEffect"); }); it("all component names are unique", () => { @@ -252,9 +255,11 @@ describe("generateTypeStubs", () => { expect(Object.keys(stubs).length).toBeGreaterThan(0); }); - it("returns at least 18 type stubs", () => { + it("returns at least 15 type stubs", () => { const stubs = generateTypeStubs(); - expect(Object.keys(stubs).length).toBeGreaterThanOrEqual(18); + // Threshold lowered from 18 after removing memo, memoFn, createSignal, + // createMemo, createEffect in 1.4.0. + expect(Object.keys(stubs).length).toBeGreaterThanOrEqual(15); }); it("includes signal type stub", () => { @@ -318,12 +323,13 @@ describe("generateTypeStubs", () => { expect(stubs["Suspense"]).toContain("declare function Suspense"); }); - it("includes SolidJS-style alias stubs", () => { + it("does NOT include stubs for removed SolidJS-style aliases", () => { const stubs = generateTypeStubs(); - expect(stubs["createSignal"]).toBeDefined(); - expect(stubs["createSignal"]).toContain("declare function createSignal"); - expect(stubs["createMemo"]).toBeDefined(); - expect(stubs["createEffect"]).toBeDefined(); + expect(stubs["createSignal"]).toBeUndefined(); + expect(stubs["createMemo"]).toBeUndefined(); + expect(stubs["createEffect"]).toBeUndefined(); + expect(stubs["memo"]).toBeUndefined(); + expect(stubs["memoFn"]).toBeUndefined(); }); it("includes DynamicComponent and registerComponent stubs", () => { diff --git a/tests/memo.test.ts b/tests/memo.test.ts index e8e6df9..07e57b7 100644 --- a/tests/memo.test.ts +++ b/tests/memo.test.ts @@ -1,21 +1,9 @@ -import { describe, expect, it } from "vitest"; -import { memo } from "../src/core/signals/memo"; -import { signal } from "../src/core/signals/signal"; +// memo() was removed in 1.4.0 (it was a one-line forward for derived()). +// This test file is kept as an empty placeholder. +import { describe, it } from "vitest"; -describe("memo", () => { - it("should memoize computed values", () => { - const [count] = signal(5); - const doubled = memo(() => count() * 2); - expect(doubled()).toBe(10); - }); - - it("should recompute when dependencies change", async () => { - const [count, setCount] = signal(3); - const doubled = memo(() => count() * 2); - - expect(doubled()).toBe(6); - setCount(7); - await Promise.resolve(); - expect(doubled()).toBe(14); +describe("memo (removed in 1.4.0)", () => { + it("memo is gone — use derived() directly", () => { + // no-op }); }); diff --git a/tests/memoFn.test.ts b/tests/memoFn.test.ts index b8cefc0..0fced00 100644 --- a/tests/memoFn.test.ts +++ b/tests/memoFn.test.ts @@ -1,25 +1,9 @@ -import { describe, expect, it } from "vitest"; -import { memoFn } from "../src/core/signals/memoFn"; -import { signal } from "../src/core/signals/signal"; +// memoFn() was removed in 1.4.0 (it was a one-line forward for derived()). +// This test file is kept as an empty placeholder. +import { describe, it } from "vitest"; -describe("memoFn", () => { - it("should return a memoized callback", () => { - const [multiplier] = signal(2); - const multiply = memoFn(() => (x: number) => x * multiplier()); - - const fn = multiply(); - expect(fn(5)).toBe(10); - }); - - it("should update callback when dependencies change", async () => { - const [multiplier, setMultiplier] = signal(2); - const multiply = memoFn(() => (x: number) => x * multiplier()); - - expect(multiply()(4)).toBe(8); - - setMultiplier(3); - await Promise.resolve(); - - expect(multiply()(4)).toBe(12); +describe("memoFn (removed in 1.4.0)", () => { + it("memoFn is gone — use derived() directly", () => { + // no-op }); }); diff --git a/tests/primitives.test.ts b/tests/primitives.test.ts index 5b4d87b..3707ca9 100644 --- a/tests/primitives.test.ts +++ b/tests/primitives.test.ts @@ -1,44 +1,10 @@ -import { describe, expect, it } from "vitest"; -import { createEffect, createMemo, createSignal } from "../src/patterns/primitives"; - -describe("createSignal", () => { - it("should create a reactive signal", () => { - const [count, setCount] = createSignal(0); - expect(count()).toBe(0); - setCount(5); - expect(count()).toBe(5); - }); - - it("should support updater function", () => { - const [val, setVal] = createSignal(10); - setVal((prev) => prev + 5); - expect(val()).toBe(15); - }); -}); - -describe("createMemo", () => { - it("should derive values from signals", async () => { - const [a, setA] = createSignal(3); - const doubled = createMemo(() => a() * 2); - expect(doubled()).toBe(6); - - setA(7); - await Promise.resolve(); - expect(doubled()).toBe(14); - }); -}); - -describe("createEffect", () => { - it("should run effect on dependency change", () => { - const [count, setCount] = createSignal(0); - let observed = -1; - - createEffect(() => { - observed = count(); - }); - - expect(observed).toBe(0); - setCount(42); - expect(observed).toBe(42); +// createSignal / createMemo / createEffect were removed in 1.4.0 (they +// were one-line forwards for signal / derived / effect). This test file +// is kept as an empty placeholder to avoid a delete in this change set. +import { describe, it } from "vitest"; + +describe("primitives (removed in 1.4.0)", () => { + it("createSignal / createMemo / createEffect are gone — use signal / derived / effect", () => { + // no-op }); }); diff --git a/tests/types.test.ts b/tests/types.test.ts index e12700f..76c09e2 100644 --- a/tests/types.test.ts +++ b/tests/types.test.ts @@ -11,8 +11,6 @@ import { array } from "../src/core/signals/array"; import { deepSignal } from "../src/core/signals/deepSignal"; import { derived } from "../src/core/signals/derived"; import { effect } from "../src/core/signals/effect"; -import { memo } from "../src/core/signals/memo"; -import { memoFn } from "../src/core/signals/memoFn"; import { ref } from "../src/core/signals/ref"; import { signal } from "../src/core/signals/signal"; import { store } from "../src/core/signals/store"; @@ -262,26 +260,6 @@ describe("deepSignal type inference", () => { }); }); -// ── memo / memoFn inference ────────────────────────────────────────────── - -describe("memo type inference", () => { - it("infers return type from factory", () => { - const [count] = signal(0); - const doubled = memo(() => count() * 2); - expectType(doubled()); - expect(doubled()).toBe(0); - }); -}); - -describe("memoFn type inference", () => { - it("infers callback type", () => { - const handler = memoFn(() => (x: number) => x * 2); - const fn = handler(); - expectType(fn(5)); - expect(fn(5)).toBe(10); - }); -}); - // ── effect inference ─────────────────────────────────────────────────────── describe("effect type inference", () => {