') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); upgrade: migrate to Solid 2.0 and latest MotionOne by davedbase · Pull Request #22 · solidjs-community/solid-motionone · GitHub
Skip to content

upgrade: migrate to Solid 2.0 and latest MotionOne - #22

Open
davedbase wants to merge 7 commits into
mainfrom
update/solid2
Open

upgrade: migrate to Solid 2.0 and latest MotionOne#22
davedbase wants to merge 7 commits into
mainfrom
update/solid2

Conversation

@davedbase

Copy link
Copy Markdown
Member

This is a two-part migration:

Solid.js 1.x → 2.0-rc.3. Solid 2.0 removes use: directives, Context.Provider JSX, batch(), and solid-js/web/solid-js/store import paths, and changes effect/context semantics in ways that touched every file in src/:

  • use:motion is now a ref factory: <div ref={motion(() => ({...}))} /> instead of <div use:motion={{...}} />.
  • ParentContext/PresenceContext now use direct-call JSX (<Context value={...}>) instead of .Provider, and are read via a small tryUseContext helper since Solid 2.0's useContext now throws instead of returning undefined when there's no provider.
  • Internal effects were restructured for Solid 2.0's split compute/apply model and its microtask-batched (not synchronous) scheduling.
  • @solid-primitives/{props,refs,transition-group} bumped to their Solid-2-compatible next prereleases; babel-preset-solid pinned to 2.0.0-rc.2 via a pnpm.overrides entry (tsup-preset-solid's bundled esbuild-plugin-solid otherwise hardcodes the 1.x preset and would silently ship a broken build importing the now-removed solid-js/web path).

Motion engine: @motionone/dom → Motion 13.1.1. The old engine is unmaintained (frozen since June 2024) and its core primitives (createMotionState, createStyles, mountedStates) have no equivalent in modern Motion — that package now centers on a much heavier VisualElement/animationState system built for React. Rather than pull in that whole machinery, src/engine.ts is a new, lean hand-rolled adapter built on motion-dom/framer-motion/dom's stable primitives (animate(), hover(), press(), inView()), reproducing the same mount/update/exit lifecycle, variant resolution, and gesture composition the old engine provided — same public API, same easing prop name (translated internally to modern Motion's ease).

One confirmed behavior change: SSR/style output no longer uses @motionone/dom's CSS-custom-property trick for independent transforms (--motion-translateX: 100px; transform: translateX(var(--motion-translateX))); modern Motion composes a direct transform string instead (transform: translateX(100px)). Functionally equivalent, but byte-different output if anything was depending on those --motion-* variable names directly.

New: Storybook rig..storybook/ + stories/ add 28 interactive stories (storybook-solidjs-vite@10.7.1 + the new @solidjs/vite-plugin, since the legacy vite-plugin-solid doesn't support Solid 2.0) covering every documented feature — including hover+press composition, inView, and nested variant inheritance, none of which had prior test coverage. Run with pnpm run storybook.

Notable fix

Found and fixed a real bug via the Storybook/real-browser verification pass (not caught by the jsdom test suite): a sibling Motion component can get constructed and briefly mount/unmount before its real mount, as a structural side effect of how <Show>/createSwitchTransition read reactive sources during a PresenceexitBeforeEnter cycle. The engine's animation-cancellation state was previously a single shared closure per component, so that phantom cycle's stale cleanup could silently cancel the real mount's in-flight animation with no error — hanging exitBeforeEnter indefinitely. Fixed by scoping cleanup state to a per-mount-generation context in engine.ts, so a superseded mount cycle can never reach into a newer one.

Breaking changes

  • Requires solid-js@^2.0.0-rc.0 (Solid 2.0 is currently in beta/RC).
  • use:motion={{...}}ref={motion(() => ({...}))} for the raw directive API (the <Motion>/<Presence> component API is unchanged).
  • SSR/DOM style output for transforms changed format (see above) — not expected to be user-visible in normal usage, but worth a semver-major bump.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@davedbase