Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/marker-solid2-migration.md
Original file line number Diff line number Diff line change
@@ -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).
14 changes: 10 additions & 4 deletions packages/marker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 <mark>{text()}</mark>;
});
```

> **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.
Expand Down
6 changes: 3 additions & 3 deletions packages/marker/package.json
Original file line number Diff line number Diff line change
@@ -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 <gthetarnav@gmail.com>",
"contributors": [],
Expand Down Expand Up @@ -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"
}
}
16 changes: 11 additions & 5 deletions packages/marker/src/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -79,10 +79,16 @@ export function createMarker<T>(
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);
Expand Down
4 changes: 3 additions & 1 deletion packages/marker/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.