Skip to content

feat(analyzer): analyse composables in .uf.ts modules - #71

Merged
alexgrozav merged 3 commits into
milestone-m5-uxf-443from
composables-analysis-uxf-458
Oct 28, 2026
Merged

alexgrozav merged 3 commits into
milestone-m5-uxf-443from
composables-analysis-uxf-458

Conversation

@alexgrozav

@alexgrozav alexgrozav commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What changed

The analyser reads a .uf.ts module of composables (ADR-0069).

  • The entry. analyze() takes a .uf.ts module as well as a .uf.tsx one. A .uf.ts module holds no component. Each named export use[A-Z]…, a function declaration, is a composable, lowered into UfModule.composables.
  • The body. It is declared and lowered by the component setup's own pass (declareSetup), with its parameters in place of props and a returned shorthand object in place of JSX. A component's rules apply to it as they do to a setup.
  • Parameters. Each is a name with a type, read through an argument binding as a destructured prop is read. Its type, written in place, gives its kind: a function type without parameters is a getter, any other function type a callback, and anything else a value. A function type through an alias or a union is UF2035, and a name a target reserves is UF2003. Watching a getter by name is UF2020, with a safe fix to () => value().
  • The API. The module's API lists a ComposableApi per composable: each parameter's kind and type, each member's kind and its type where the source writes one, whether a returned function is async, and pure or client by its summary (a function that calls a callback is client code, and UF2014 where a getter or an initial value calls it), and hasEffects where the setup holds a watcher or a lifecycle hook.
  • UF2035. Each form ADR-0069 rejects is reported at its span, once, with a test each:
    • a call of useId, useTemplateRef, provide, inject or a define* macro;
    • a call of another composable, imported from a .uf.ts or declared in the module;
    • a default export, declared or listed;
    • a return of any other shape: no return, a ref, an array, a member that is not a shorthand property, or a name that is not the setup's own.
    • It also covers a member returned twice, a this parameter, a value import of a .uf.ts module that no composable calls, a parameter without a type, with a default or written as a pattern, a callback read outside client code, a use* function the module keeps or writes as a value, an export named otherwise, an async composable, and a module that exports no composable.
  • Type specifiers of a .uf.ts import, inline or whole, are re-imported (typeImports).
  • An injection key in a .uf.ts module is UF1002: it stays in the .uf.tsx module that provides it (ADR-0054).

The call site and compile()'s handling of a .uf.ts source come in a later change. Framework output: no output changes on any target, and no corpus case changes.

Checklist

  • The title follows Conventional Commits, as in feat(web): sell the next framework on the homepage.
  • pnpm format:check, pnpm lint, pnpm check-types and pnpm test pass. The first three pass. The analyser's, the compiler's and the repo tests pass, and so does the full integration suite on all seven targets, with the parity matrix green. The root pnpm test was not run as one command.
  • Changes to a published package: a changeset from pnpm changeset (see .changeset/README.md). Not needed: every package is private.
  • Homepage changes: pnpm build, then pnpm --filter web lighthouse, still scores at least 0.95 in every category. Not needed: no homepage change.

A `.uf.ts` module's named `use*` exports are composables (ADR-0069). Each
body is declared and lowered by the component setup's own pass, with
parameters read as `argument` bindings in place of props and a returned
shorthand object in place of JSX. The module's IR holds `UfComposable`,
and its API a `ComposableApi` per composable, with each member's kind,
the flags of its functions and `hasEffects`.

UF2035 reports each form ADR-0069 rejects, at its span: the APIs that
need a component, a call of another composable, a default export, a
return of any other shape, and parameters a target cannot read by kind.

Co-authored-by: multica-agent <github@multica.ai>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://composables-analysis-uxf-458-unframework.blue-water-0397.workers.dev (commit d5b273b)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://188bcd83-unframework.blue-water-0397.workers.dev d5b273b 2026-10-28T09:52:41.132Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://73790bd7-unframework.blue-water-0397.workers.dev 286f009 2026-10-28T09:31:35.275Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://873de237-unframework.blue-water-0397.workers.dev e02172e 2026-10-28T08:55:39.861Z Visit the dashboard ↗

alexgrozav and others added 2 commits October 28, 2026 11:25
A reserved parameter name is UF2003, a member returned twice, a function type written through an alias or a union, a `this` parameter and a value import of a `.uf.ts` module that no composable calls are UF2035. Type specifiers of a `.uf.ts` import are re-imported. A function that calls a callback is client code: impure where a getter or an initial value calls it (UF2014), and `client` in the API.

Co-authored-by: multica-agent <github@multica.ai>
A read of a composable's parameter records no narrowing: only a prop's
or a ref's value's read narrows in the IR. A parameter typed `Function`
or a call signature, in place or through the module's aliases and
interfaces, is UF2035, as a function type through an alias is.

The fix for a member returned twice starts at the property before it,
and UF2020's fix calls an optional getter with `?.()`.

Co-authored-by: multica-agent <github@multica.ai>
@alexgrozav
alexgrozav merged commit ab25e99 into milestone-m5-uxf-443 Oct 28, 2026
49 checks passed
@alexgrozav
alexgrozav deleted the composables-analysis-uxf-458 branch October 28, 2026 10:30
Sign up for free to 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