From dfa24227bdf87de40ee87294438ed5f3071cdab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AE=B6=E7=A5=BA?= Date: Fri, 19 Jul 2024 20:42:21 +0800 Subject: [PATCH] fix(#66): signals as refs --- playground/pnpm-lock.yaml | 8 +- playground/src/App.tsx | 3 +- pnpm-lock.yaml | 8 +- src/babel/core/transform-jsx.ts | 20 +++- tests/client/__snapshots__/esm.test.ts.snap | 110 ++++++++++++++++++++ tests/client/esm.test.ts | 53 ++++++++++ 6 files changed, 188 insertions(+), 14 deletions(-) diff --git a/playground/pnpm-lock.yaml b/playground/pnpm-lock.yaml index d2d672d..2cea5c5 100644 --- a/playground/pnpm-lock.yaml +++ b/playground/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - dependencies: solid-js: specifier: link:../node_modules/solid-js @@ -1934,3 +1930,7 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/playground/src/App.tsx b/playground/src/App.tsx index 31376df..a651728 100644 --- a/playground/src/App.tsx +++ b/playground/src/App.tsx @@ -2,6 +2,7 @@ import { createSignal, onCleanup, onMount } from 'solid-js'; export function App() { const [count, setCount] = createSignal(0); + const [, setEl] = createSignal(); function increment() { setCount(c => c + 1); @@ -19,7 +20,7 @@ export function App() { return ( <> -

Count: {count()}

+

Count: {count()}

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2251ed4..391d7f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - dependencies: '@babel/generator': specifier: ^7.23.6 @@ -1666,3 +1662,7 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/src/babel/core/transform-jsx.ts b/src/babel/core/transform-jsx.ts index 70dabec..8f19e4c 100644 --- a/src/babel/core/transform-jsx.ts +++ b/src/babel/core/transform-jsx.ts @@ -77,6 +77,10 @@ function extractJSXExpressionFromRef( let replacement: t.Expression; if (unwrappedIdentifier) { const arg = expr.scope.generateUidIdentifier('arg'); + const binding = expr.scope.getBinding(unwrappedIdentifier.name); + const cannotAssignKind = ['const', 'module']; + const isConst = binding && cannotAssignKind.includes(binding.kind); + replacement = t.arrowFunctionExpression( [arg], t.blockStatement([ @@ -91,11 +95,17 @@ function extractJSXExpressionFromRef( t.callExpression(unwrappedIdentifier, [arg]), ), ]), - t.blockStatement([ - t.expressionStatement( - t.assignmentExpression('=', unwrappedIdentifier, arg), - ), - ]), + // fix the new usage of `ref` attribute, + // if use `Signals as refs`, the `else` branch will throw an errow with `Cannot assign to "setter" because it is a constant` message + // issue: https://github.com/solidjs/solid-refresh/issues/66 + // docs: https://docs.solidjs.com/concepts/refs#signals-as-refs + isConst + ? null + : t.blockStatement([ + t.expressionStatement( + t.assignmentExpression('=', unwrappedIdentifier, arg), + ), + ]), ), ]), ); diff --git a/tests/client/__snapshots__/esm.test.ts.snap b/tests/client/__snapshots__/esm.test.ts.snap index b0c5ec2..cbc3f91 100644 --- a/tests/client/__snapshots__/esm.test.ts.snap +++ b/tests/client/__snapshots__/esm.test.ts.snap @@ -1130,6 +1130,116 @@ if (import.meta.hot) { }" `; +exports[`esm (client, non-hydratable) > fix build > refs > should work with a function 1`] = ` +"import { template as _$template } from "solid-js/web"; +import { createComponent as _$createComponent } from "solid-js/web"; +import { use as _$use } from "solid-js/web"; +var _tmpl$ = /*#__PURE__*/_$template(\`
Comp\`); +import { $$component as _$$component } from "solid-refresh"; +import { $$refresh as _$$refresh } from "solid-refresh"; +import { $$registry as _$$registry } from "solid-refresh"; +const _REGISTRY = _$$registry(); +const Comp_1 = _$$component(_REGISTRY, "Comp_1", _props => /*@refresh jsx-skip*/(() => { + var _el$ = _tmpl$(); + var _ref$ = _props.v0; + typeof _ref$ === "function" ? _$use(_ref$, _el$) : _props.v0 = _el$; + return _el$; +})(), { + location: "example.jsx:4:19", + signature: "6f76290" +}); +const Comp = _$$component(_REGISTRY, "Comp", () => { + let el; + return /*@refresh jsx-skip*/_$createComponent(Comp_1, { + v0: _el => el = _el + }); +}, { + location: "example.jsx:2:23", + signature: "726057ca" +}); +if (import.meta.hot) { + _$$refresh("esm", import.meta.hot, _REGISTRY); +}" +`; + +exports[`esm (client, non-hydratable) > fix build > refs > should work with a mutable variable 1`] = ` +"import { template as _$template } from "solid-js/web"; +import { createComponent as _$createComponent } from "solid-js/web"; +import { use as _$use } from "solid-js/web"; +var _tmpl$ = /*#__PURE__*/_$template(\`
Comp\`); +import { $$component as _$$component } from "solid-refresh"; +import { $$refresh as _$$refresh } from "solid-refresh"; +import { $$registry as _$$registry } from "solid-refresh"; +const _REGISTRY = _$$registry(); +const Comp_1 = _$$component(_REGISTRY, "Comp_1", _props => /*@refresh jsx-skip*/(() => { + var _el$ = _tmpl$(); + var _ref$ = _props.v0; + typeof _ref$ === "function" ? _$use(_ref$, _el$) : _props.v0 = _el$; + return _el$; +})(), { + location: "example.jsx:4:19", + signature: "6f76290" +}); +const Comp = _$$component(_REGISTRY, "Comp", () => { + let el; + return /*@refresh jsx-skip*/_$createComponent(Comp_1, { + v0: _arg => { + if (typeof el === "function") { + el(_arg); + } else { + el = _arg; + } + } + }); +}, { + location: "example.jsx:2:23", + signature: "d3db0f89" +}); +if (import.meta.hot) { + _$$refresh("esm", import.meta.hot, _REGISTRY); +}" +`; + +exports[`esm (client, non-hydratable) > fix build > signals as refs > should work 1`] = ` +"import { template as _$template } from "solid-js/web"; +import { createComponent as _$createComponent } from "solid-js/web"; +import { use as _$use } from "solid-js/web"; +var _tmpl$ = /*#__PURE__*/_$template(\`
Comp\`); +import { $$component as _$$component } from "solid-refresh"; +import { $$refresh as _$$refresh } from "solid-refresh"; +import { $$registry as _$$registry } from "solid-refresh"; +import { createSignal } from 'solid-js'; +const _REGISTRY = _$$registry(); +const Comp_1 = _$$component(_REGISTRY, "Comp_1", _props => /*@refresh jsx-skip*/(() => { + var _el$ = _tmpl$(); + var _ref$ = _props.v0; + typeof _ref$ === "function" ? _$use(_ref$, _el$) : _props.v0 = _el$; + return _el$; +})(), { + location: "example.jsx:6:19", + signature: "6f76290" +}); +const Comp = _$$component(_REGISTRY, "Comp", () => { + const [el, setEl] = createSignal(); + return /*@refresh jsx-skip*/_$createComponent(Comp_1, { + v0: _arg => { + if (typeof setEl === "function") { + setEl(_arg); + } + } + }); +}, { + location: "example.jsx:4:23", + signature: "57db44c6", + dependencies: () => ({ + createSignal + }) +}); +if (import.meta.hot) { + _$$refresh("esm", import.meta.hot, _REGISTRY); +}" +`; + exports[`esm (client, non-hydratable) > fix render > @refresh reload > should work 1`] = ` "import { createComponent as _$createComponent } from "solid-js/web"; import { $$decline as _$$decline } from "solid-refresh"; diff --git a/tests/client/esm.test.ts b/tests/client/esm.test.ts index 841c019..c6a38ef 100644 --- a/tests/client/esm.test.ts +++ b/tests/client/esm.test.ts @@ -810,4 +810,57 @@ describe('esm (client, non-hydratable)', () => { }); }); }); + describe('fix build', () => { + describe('refs', () => { + it('should work with a mutable variable', async () => { + expect( + await transform( + ` + const Comp = () => { + let el; + return
Comp
; + } + `, + 'esm', + 'client', + false, + ), + ).toMatchSnapshot(); + }); + it('should work with a function', async () => { + expect( + await transform( + ` + const Comp = () => { + let el; + return
el = _el}>Comp
; + } + `, + 'esm', + 'client', + false, + ), + ).toMatchSnapshot(); + }); + }); + describe('signals as refs', () => { + it('should work', async () => { + expect( + await transform( + ` + import { createSignal } from 'solid-js'; + + const Comp = () => { + const [el, setEl] = createSignal(); + return
Comp
; + } + `, + 'esm', + 'client', + false, + ), + ).toMatchSnapshot(); + }); + }); + }); });