Repository navigation
Conversation
|
Merged the review fixes from #14, #15 and #16 in. The I re-checked the template in the dev server: the module is React Compiler output, it destructures |
fdba401 to
2316608
Compare
48d3607 to
a00c0c5
Compare
|
Restacked on
The review-fix merges are folded into the branches' own commits. The code is unchanged apart from #21's changes. Only On every branch, |
2316608 to
458872c
Compare
a00c0c5 to
90d812f
Compare
…^:memo fns with its "use memo" directive (for's iterators call themselves through a letfn local in them), React's array hooks' values destructured by index, a react-compiler template
458872c to
c97bad1
Compare
90d812f to
68faeb1
Compare
|
putting this on hold for now for further consideration |
Adds a
react-compilertemplate tocreate-clojurific, and what it needs in Clojurific: the Vite plugin runs React Compiler, and^:memofns get React Compiler's"use memo"directive.Stacking: based on #16 (destructuring) →
main, which has #13 (#xliterals), #14 (const/let) and #21 (munged-name collisions). #15'sforchange is included here (see below).Changes
Vite plugin,
reactCompileroption (vite-plugin-cljs/react-compiler.js):babel-plugin-react-compiler(with@babel/core, both installed in the project) on the compiled namespaces, with source maps.trueor React Compiler's own options. By default (compilationMode: 'annotation') only modules with^:memofns ("use memo"functions) are transformed.environment.moduleTypeProviderdeclaring the JSX runtimes (react/jsx-runtime,preact/...,hono/jsx/...) andcljf.jsx's compiled module the way React Compiler declaresReact.createElement: arguments frozen, frozen elements returned. Without that, React Compiler treatsjsx()as an unknown, possibly mutating call and caches nothing apart; with it, it memoizes per element as it does JSX syntax. A user's ownmoduleTypeProvideris consulted first.Compiler,
^:memo: in ES module output, a^:memofn starts with React Compiler's"use memo"directive:(defn ^:memo App [] ...)or(fn ^:memo Row [props] ...). It's read from the name's metadata, as^:asyncis. Fns nested in it aren't marked. Components and hooks are fns of one fixed arity, so a multi-arity or variadic^:memodefn gets a:memo-fnwarning and no directive. Classic output ignores^:memo.forin^:memofns (from ES module output: for's iterators call themselves through a letfn local #15, closed): in a^:memofn,for's iterators are bound withletfn*to an anonymous fn and call themselves through that local. A named function expression calling itself makes React Compiler reject the component ("Expected value kind to be initialized").^:memomarks the analyzer env:memo, which theformacro reads. Outside^:memofns,foris unchanged. Inletfn*, an anonymous fn now refers to itself through its binding (before, the self-call resolved to a namespace var that doesn't exist);letfn's own self-named fns are unchanged.React's array hooks destructured by index (moved here from ES module output: destructure #js [] arrays by index #16):
cljs.analyzer/js-array-init?also counts the values ofreact's (andpreact/hooks',preact/compat's)useState,useReducer,useTransition,useActionState,useOptimisticanduseFormState. Their[value setter]is read with(v?.[i] ?? null)instead ofnth, whose call React Compiler treats as mutating the array: in a two-useStatecomponent it memoized nothing (0 slots) without this, and 10 slots with it. The check is by module and fn, so another module'suseStategoes throughnth, as douseMemo's values (any value) and loop locals.Template
react-compiler:reactCompiler: trueinvite.config.ts;babel-plugin-react-compilerand@babel/coreas dev dependencies;^:memoAppandRowcomponents, built with#x,foranduseState.It's in the interactive menu ("React + React Compiler"), the create-clojurific README and its tests, and in
script/test-launcher, which also checks that the bundle has React Compiler's memo cache.Verified
App; the rows' React props objects stay identical across the clicks, so theRows aren't re-rendered.vite build+vite preview: the bundle contains the memo cache and the app works.Rowrenders: 15 re-renders after 5 unrelated state changes without React Compiler, 0 with it.Tests
script/test-esm: 0 failures, 0 errors. A new test covers^:memoondefnand namedfns, fns nested in them (not marked), a string in the body (not a directive), and the warning for a multi-arity^:memofn.node --test(launcher, plugin, create-clojurific): 69 pass, 1 skipped. The new React Compiler transform tests run withCLJF_REACT_COMPILER_DIRpointing at a project that has the Babel packages installed, and are skipped otherwise. They pass when run that way.script/test-launcher.Notes