diff --git a/.changeset/marker-solid2-migration.md b/.changeset/marker-solid2-migration.md new file mode 100644 index 000000000..ae8ad9dbb --- /dev/null +++ b/.changeset/marker-solid2-migration.md @@ -0,0 +1,17 @@ +--- +"@solid-primitives/marker": major +--- + +Migrate to Solid.js v2.0 (beta.10) + +## Breaking Changes + +**Peer dependency**: `solid-js@^2.0.0-beta.10` is now required. + +### `createMarker` + +**`createRoot` second parameter removed** — In Solid 1.x the second argument to `createRoot` was a parent owner. In Solid 2.0 the API changed to an options object; the parent is now implicitly the current reactive context. The implementation now uses `runWithOwner(owner, () => createRoot(...))` to explicitly parent cached element roots to the marker's creation-time owner, preserving the existing lifecycle semantics. + +**Signal `ownedWrite: true`** — In Solid 2.0, writing to a signal from inside an owned reactive scope (component body, `createRoot` callback, effect compute phase) throws by default. The internal per-match signal is now created with `{ ownedWrite: true }` so that `set()` can be called when the marker function is invoked from any reactive context. + +**Deferred signal reads after `set()`** — In Solid 2.0 all signal writes are deferred until the next microtask flush. When a cached match element is reused and its text is updated via `set(newText)`, reading the signal accessor immediately returns the previously committed value. Call `flush()` from `solid-js` before reading a reused element's accessor if synchronous access is required (e.g. in tests). diff --git a/packages/marker/README.md b/packages/marker/README.md index e51eb95bb..b8c8018c5 100644 --- a/packages/marker/README.md +++ b/packages/marker/README.md @@ -20,6 +20,8 @@ yarn add @solid-primitives/marker pnpm add @solid-primitives/marker ``` +Requires `solid-js` `^2.0.0-beta.10`. + ## How to use it `createMarker` creates a function for marking parts of a string that match a regex. Each match will be mapped by `mapMatch` callback and returned as an array of strings and mapped values. @@ -42,21 +44,25 @@ const highlight = createMarker(matchedText => { The `mapMatch` callback is **not** reactive. The value returned by it is cached and reused between marker calls. This is useful for performance reasons, but it also means that the callback should handle the matched text as an accessor. -It behaves similarly to the `mapFn` param of `indexArray`, where the returned element is reused for different values. +It behaves similarly to the `mapFn` param of `mapArray`, where the returned element is reused for different values. Any computations created in that callback will be disposed when `createMarker` gets disposed, not on each marker call, because the results are cached between calls. ```tsx const mark = createMarker(text => { // you can safely create computations here - createEffect(() => { - console.log(text()); - }); + createEffect( + () => text(), + t => console.log(t), + ); return {text()}; }); ``` +> **Note:** In Solid 2.0, `createEffect` requires a split `(compute, apply)` signature. +> Reading `text()` in JSX is reactive — it updates automatically when the match text changes. + ## Caching The marker callback is cached between calls. diff --git a/packages/marker/package.json b/packages/marker/package.json index 3fb48c3ce..8839e664f 100644 --- a/packages/marker/package.json +++ b/packages/marker/package.json @@ -1,6 +1,6 @@ { "name": "@solid-primitives/marker", - "version": "0.2.2", + "version": "1.0.0", "description": "A reactive primitive for marking matching parts of a string.", "author": "Damian Tarnawski ", "contributors": [], @@ -53,9 +53,9 @@ "test:ssr": "pnpm run vitest --mode ssr" }, "peerDependencies": { - "solid-js": "^1.6.12" + "solid-js": "^2.0.0-beta.10" }, "devDependencies": { - "solid-js": "^1.9.7" + "solid-js": "2.0.0-beta.10" } } diff --git a/packages/marker/src/index.ts b/packages/marker/src/index.ts index a0a69584a..d530ee97e 100644 --- a/packages/marker/src/index.ts +++ b/packages/marker/src/index.ts @@ -1,4 +1,4 @@ -import { type Accessor, createRoot, createSignal, getOwner, onCleanup } from "solid-js"; +import { type Accessor, createRoot, createSignal, getOwner, onCleanup, runWithOwner } from "solid-js"; const SANITIZE_REGEX = /[^\w\s]/g, SPLIT_WORDS_REGEX = /\s+/g, @@ -79,10 +79,16 @@ export function createMarker( reuseIndex++; reusable.set(matchText); } else { - createRoot(dispose => { - const [text, set] = createSignal(matchText); - reusable = { el: mapMatch(text), set, dispose }; - }, owner); + // In Solid 2.0 createRoot's second param changed from `owner` to `options`. + // runWithOwner parents the new root to the marker's creation-time owner. + runWithOwner(owner, () => { + createRoot(dispose => { + // ownedWrite: true so set() can be called from reactive contexts + // (createRoot callbacks, component bodies, effect compute phases). + const [text, set] = createSignal(matchText, { ownedWrite: true }); + reusable = { el: mapMatch(text), set, dispose }; + }); + }); } lastIndex !== match.index && parts.push(string.slice(lastIndex, match.index)); parts.push(reusable!.el); diff --git a/packages/marker/test/index.test.ts b/packages/marker/test/index.test.ts index a2abdf7ba..198c47998 100644 --- a/packages/marker/test/index.test.ts +++ b/packages/marker/test/index.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "vitest"; -import { createRoot } from "solid-js"; +import { createRoot, flush } from "solid-js"; import { createMarker, makeSearchRegex } from "../src/index.js"; describe("makeSearchRegex", () => { @@ -125,6 +125,8 @@ describe("createMarker", () => { }); createRoot(dispose => { const result = mark("Hello solid!", /\w+/g); + // Reused signals have deferred writes in Solid 2.0 — flush before reading. + flush(); expect(result).toEqual([expect.any(Function), " ", expect.any(Function), "!"]); expect((result[0] as Function)()).toBe("Hello"); expect((result[2] as Function)()).toBe("solid"); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f59c63a9c..ad86d6e67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -537,8 +537,8 @@ importers: packages/marker: devDependencies: solid-js: - specifier: ^1.9.7 - version: 1.9.7 + specifier: 2.0.0-beta.10 + version: 2.0.0-beta.10 packages/masonry: dependencies: