Contributor guide #
Project layout #
packages/jsx-content-mapperimplements framework-independent.gtsxmapping, the generator-preserving runtime, the TypeScript content mapper server, and the Vite plugin.packages/foldkit-jsxadapts Foldkit's typed HTML builders to a classic JSX factory. Foldkit-specific behavior belongs here.examples/foldkitis the runnable application demonstrating.gtsxViews and Submodels.apps/vsc-extensionregisters.gtsxwith TypeScript Native Preview and bundles a mapper for loose files. Configured projects keep their own mapper package and options.
Keep the root README focused on users: what the project does, a small example, and setup. Put contributor instructions here and detailed package API guidance in the package READMEs.
Implementation conventions #
- Use Babel's TypeScript/JSX parser and JSX child normalization. Do not introduce a handwritten JSX parser.
- Preserve all generator type parameters, including TypeScript's inferred
unknownor Effect'sanynext type. Do not replace them withnever. - Keep the mapper independent of Foldkit. Its classic mode emits configurable
factory(tag, props, ...children)calls; framework adapters interpret props. - Derive Foldkit prop types and constructors from
HtmlBuilder<Message>rather than copying an attribute table. Prop names use Foldkit's exact casing, such asOnClick,Class, andAriaLabel. - Bind
createJsx(h)inside each View using that View's builder. Retain Foldkit's typed Submodel boundaries and message routing. - Keep the Vite and TypeScript content mapper options in sync. Changes to mapped
code must preserve diagnostics and source locations in the original
.gtsxfiles.
Toolchain #
Use the Node version in .nvmrc and PNPM 12 for this workspace. CI reads Node from
that file and PNPM from the root packageManager field. Install dependencies
from the repository root:
nvm install
nvm use
pnpm install
The mapper requires Node.js ^22.18.0 || >=24.11.0. Package builds use stable
TypeScript 6.0.3; .gtsx checks use the pinned TypeScript
7.1.0-dev.20261002.1 nightly through the typescript-next alias. Content mappers
are experimental and currently require TypeScript 7.1. Keep the explicit
compiler paths in package scripts so the stable and nightly executables do not
get mixed up. The example and integration tests use Effect 4.0.0.
Checks #
Root commands use Nx to order dependencies and cache project checks:
pnpm build
pnpm typecheck
pnpm test
pnpm lint
pnpm format
pnpm format:check
pnpm check
pnpm test runs all regular Vitest suites.
pnpm lint runs ESLint for the root, both libraries, and the extension, plus
Zizmor for GitHub Actions. Examples are excluded from ESLint. Install
Zizmor to run pnpm lint, pnpm lint:ci,
or pnpm check locally; CI uses a dedicated, pinned Zizmor action.
Formatting includes .gtsx sources and ignores generated output and the PNPM lockfile.
For changes relative to main, run only affected projects:
pnpm affected --base=main --head=HEAD
Nx uses the existing package scripts instead of inferring compiler commands from Vite or TypeScript configuration. This keeps the stable and nightly compiler paths explicit and lets the graph load before library builds exist. The extension also depends on the Foldkit adapter and example fixtures, so changes to those projects rerun its language-server integration tests.
Run checks appropriate to the files and behavior changed. For either library,
replace the package name below with @causeeffect/foldkit-jsx when working on
the adapter:
pnpm --filter @causeeffect/jsx-content-mapper build
pnpm --filter @causeeffect/jsx-content-mapper typecheck
pnpm --filter @causeeffect/jsx-content-mapper test
pnpm --filter @causeeffect/jsx-content-mapper lint
Mapper tests include native TypeScript 7.1 integration checks as well as runtime,
mapping, and Vite tests. Adapter tests check both Foldkit behavior and compile-time
contracts. For changes affecting the .gtsx application or its integration, use:
pnpm --filter @causeeffect/foldkit-example typecheck
pnpm --filter @causeeffect/foldkit-example test
pnpm --filter @causeeffect/foldkit-example build
The example scripts build both workspace libraries first. Its typecheck invokes
the nightly compiler with --runExternalCode, allowing it to start the mapper.
The example tests include Foldkit Scene interactions and invalid .gtsx type
fixtures. Start the browser example with:
pnpm --filter @causeeffect/foldkit-example dev
Format changed files with Prettier and check documentation-only changes without running unrelated application tests:
pnpm exec prettier --write <changed-files>
pnpm exec prettier --check <changed-files>
git diff --check
For extension changes, run:
pnpm --filter causeeffect-gtsx typecheck
pnpm --filter causeeffect-gtsx test
pnpm --filter causeeffect-gtsx lint
pnpm --filter causeeffect-gtsx test:host
pnpm --filter causeeffect-gtsx package
The extension's native LSP tests cover generator projects and the configured
Foldkit example, including its classic JSX options. The Vitest host smoke test requires
a local VS Code installation, TypeScript Native Preview, and TypeScript 7 Nightly;
it uses the installed Nightly compiler without an SDK override and invokes the
actual Enable command in temporary settings and workspace directories.
Package the VSIX after tests because test
setup rebuilds the extension's dist directory.
The root pnpm test:host command runs this separate suite through Nx. Its target
is uncached and runs after the regular extension suite. CI provisions VS Code
and both required extensions, and runs the host test when Nx marks the extension
affected.
CI and npm releases #
Pull requests targeting main and merge queues run the reusable CI workflow.
It runs formatting, ESLint, typechecks, Vitest, and builds for affected projects,
using main as the base branch and the last successful main run when available.
Zizmor checks every workflow in a dedicated job with pedantic, offline audits. A push to main
invokes the same CI from publish.yml before running Changesets.
Only @causeeffect/jsx-content-mapper and @causeeffect/foldkit-jsx publish to
npm. For changes that should release either library, add a changeset to the PR:
pnpm changeset
pnpm changeset:status
Choose the affected packages, bump types, and a changelog summary. Commit the
generated .changeset/*.md file with the change; do not bump package versions
manually. Build tooling and documentation changes that do not require a package
release do not need a changeset. Private applications and examples are excluded
from versioning and publishing.
The Changesets action creates or updates a release PR when changesets reach
main. It consumes those files, updates package versions and changelogs, and
refreshes the PNPM lockfile through pnpm release:version. Merging the release
PR runs CI and builds through Nx, then pnpm release:publish publishes missing
versions using Changesets and GitHub OIDC through PNPM's native publisher.
For a read-only registry preview:
pnpm release:plan
The repository has Actions → General → Allow GitHub Actions to create and
approve pull requests enabled. Release PRs created with GITHUB_TOKEN
do not trigger ordinary PR workflows, so the version job explicitly dispatches
CI on the release branch. CI can also be dispatched manually.
Before the first automated release, configure an npm trusted publisher for each library:
- GitHub owner:
crutchcorn - Repository:
causeeffect - Workflow filename:
publish.yml - GitHub environment:
npm - Allowed action: direct
npm publish
The workflow uses the npm GitHub environment, restricted to the main branch.
npm requires packages to exist before configuring their
trusted publisher; unpublished packages need an initial maintainer publication.
From an authenticated maintainer session, build and bootstrap them with:
pnpm build
npm publish ./packages/jsx-content-mapper --access=public --provenance=false
npm publish ./packages/foldkit-jsx --access=public --provenance=false
Local bootstrap publications disable provenance; subsequent GitHub OIDC releases
generate it. The workflow uses the pinned Node and PNPM versions and grants
id-token: write only to its publishing job.
When incremental commits are requested, commit coherent completed changes as the work progresses. Include only the files belonging to the task.