Skip to content

Latest commit

 

History

History
185 lines (115 loc) · 10.9 KB

CONTRIBUTING.md

File metadata and controls

185 lines (115 loc) · 10.9 KB

Contributing to Sycamore

First, thank you for wanting to contribute! Sycamore is completely free and open-source and could not be so without the help of contributors like you!

Check out our community's Code of Conduct and feel free to say hello on our Discord server if you like. We have a special channel dedicated to Sycamore development where you can ask questions and guidance as well as geting to know other users and contributors of Sycamore in an informal setting.

The rest of this document will contain:

  • The high-level design goals of Sycamore.
  • A brief tour of the codebase.
  • Making changes to Sycamore.
  • Setting up your dev-environment.

What we are trying to build

Sycamore is a reactive library for creating web apps in Rust and WebAssembly. We currently have the following goals:

  • Capable: Offer a complete set of tools for anyone to create webapps in Rust.
  • Intuitive: Try to be as simple and as intuitive as possible by using patterns that are natural and easy to reason about.
  • Ergonomic: Avoid boilerplate code.
  • Fast: Sycamore apps should require the minimal amount of manual optimization. We want as many use-cases to be "fast-enough" by default.
  • Productive: Fast compilation times.

Architecture

This section will give a brief tour of Sycamore's project structure and where to find what you are looking for.

Directory structure

  • Main crates (including sycamore, sycamore-reactive, etc...) are in packages/.
  • Integration tests are in packages/sycamore/tests.
  • Benchmarks are in packages/tools/bench.
  • Examples are in examples/.
  • The Sycamore website is in website/. This will eventually be moved out into a new repository.
  • The documentation is in docs/. This also contains a tool for pre-rendering the markdown files into HTML. This will likely be moved into the new website repository when its ready.

Crates

sycamore

This is the main crate which is intended to be added to the Cargo.toml of a Sycamore project. This crate re-exports most of the APIs of the other crates.

For now, this crate also includes:

  • The Builder API. We cannot move this out yet because of inter-dependence with other features.
  • Some web-related utilities such as NoSsr and NoHydrate as well as all the HTML element declarations.
  • Easing functions. This should be moved into another crate.
  • Flow-control components: Keyed and Indexed. This is because the proc-macros rely on the sycamore crate being at the root.
  • create_resource. This should probably be moved into the sycamore-futures crate.
  • Utilities for tweened signals. This should be moved into another crate along with the easing functions.
  • Suspense. This is for the same reason as the flow-control components.
  • Some utilities used by the codegen of various proc-macros.

The goal is to eventually move most of these into their own crates.

sycamore-macro

This crate contains all the proc-macro logic for view!, #[component], and #[derive(Props)].

We use syn, quote and proc-macro2 for parsing and quasi-quoting. If you are unfamiliar with implementing proc-macros in Rust, this resource is a good guide to get started.

sycamore-reactive

This is the backbone of Sycamore's reactivity system. This crate can be used stand-alone without any of the other crates.

The API is mostly inspired by SolidJS. Behind-the-scenes, we use an arena with indices to keep track of reactive nodes. This is what allows us to make everything Copyable and 'static.

Everything created inside the reactivity system is owned by the top-level Root struct. This includes the node arena as well as global state used for tracking dependencies in memos/effects. Finally, the Root is also responsible for propagating updates through the reactive graph.

This article provides a good introduction to how reactivity is implemented (albeit in JS). The reactive propagation algorithm takes inspiration from this article, modified because our reactive system is eager rather than lazy for better predictibility.

sycamore-core

This crate contains all the core utilities for Sycamore's rendering logic. This includes:

  • Runtime support for components.
  • Swappable rendering backends.
  • Hydration code. (TODO: this should proabably be moved to sycamore-web).
  • NodeRefs.
  • Runtime support for rendering dynamic views and difing fragments.

This crate is backend-agnostic, meaning that there should be no dependence on web-sys or wasm-bindgen.

sycamore-web

This crate contains all the web specific rendering logic for Sycamore. This includes, notably, DomNode, HydrateNode, and SsrNode.

sycamore-futures

A lightweight crate to choose between tokio when on the server and wasm-bindgen-futures when on the client.

sycamore-router and sycamore-router-macro

This is an implementation of a SPA router for Sycamore. This will eventually be moved into a new repository.

Making changes to Sycamore

We don't have a strict process about making changes to Sycamore but most of the times, it should look something like this.

  1. Create one of the following:
    • GitHub Discussions: This is an informal way to propose an idea and to receive community feedback. This is a good place to start when proposing a new feature to be added to Sycamore.
    • Issue: A more formal way for us to track features and bugs. Please consider looking for duplicates before opening a new issue.
    • Pull Request (PR for short): A request to merge code changes. You are welcome to start with a pull request but consider starting with either an Issue or a Discussion first for larger changes as we don't want anybody's work to be wasted if the PR does not end up being merged. On the other hand, PRs are sometimes the most effective way to propose changes. We leave it up to your judgement to decide which is most appropriate.
  2. Other community members can give reviews and comments. If your PR is ready and has not received any reviews, feel free to post your PR on our Discord server in the development channel.
  3. Once they're satisfied with the proposed changes, they leave the "Approved" review. At this point, I (@lukechu10) will make a final review and merge the PR.

Please make sure that your code is properly formatted using cargo fmt and that is passes cargo clippy. If you are contributing a new feature, we ask that you add some unit tests, and eventually a new example. If you are fixing a bug, consider adding some regression tests so that we know it won't happen again in the future!

Setting up your dev-environment

Begin with creating a fork of the sycamore repository and clone it to your machine.

git clone https://github.com/<your account>/sycamore
cd sycamore

It is strongly recommended to use the nightly channel to develop for Sycamore. You can use the following to install nightly and set it as the default for this project.

rustup toolchain add nightly
rustup override set nightly

Finally, make sure to install the wasm32-unknown-unknown target.

rustup target add wasm32-unknown-unknown --toolchain nightly

Running tests

Running tests is as simple as running cargo test --all-features. This will automatically run all the tests in all the packages, which could take quite some time!

If you are working on a specific package, say sycamore-reactive, it might be better to first cd packages/sycamore-reactive and run cargo test --all-features in there.

WASM tests

For the WASM tests specifically, you will need to have wasm-pack installed. Then run the following command and follow the instructions in the console.

cd packages/sycamore
wasm-pack test --chrome

If you want to run the tests in a headless instead, just pass the --headless flag.

Macro diagnostics snapshot tests

The proc-macro crates have additional tests for ensuring that we have good diagnostics for common errors. For this, you will need to install the MSRV version of Rust (currently 1.73).

rustup toolchain add 1.73

The macro tests are disabled by default since when running on other versions of Rust, they are usually slightly different and thus would cause a lot of churn. To enable them, set the RUN_UI_TESTS env variable to true. In addition, if you want to overwrite the existing snapshot, set the TRYBUILD env variable to overwrite. The final command would look something like:

RUN_UI_TESTS=true TRYBUILD=overwrite cargo +1.73 test

Adding an example

When adding an example, the following steps should be taken:

  1. Add your example to the examples/ folder. An easy way to create a new example is the clone an existing example and change the name in the appropriate places (folder name and Cargo.toml).
  2. Add your example to the list of examples in examples/README.md along with a small description of what your example demonstrates.

Working on the website

The website is in the website/ folder.

We use TailwindCSS to style the website. First run npm install in the website/ folder to install TailwindCSS. Then simply run trunk serve to start the dev server.

Writing docs

All the documentation is in the docs/ folder. As a general rule, you should only ever modify the docs in docs/next/ and not touch docs/versioned_docs/. The documentation in docs/next/ will be copied over to docs/versioned_docs/ when there is a new release.

When writing the docs, you can use the docs utility crate to generate the markdown files into HTML to be displayed on the website. It is recommended to run in release mode since rendering can take some time. To run the utility, simply use cargo run --release in the docs/ folder.

Running benchmarks

We have a very basic benchmark setup in packages/tools/bench. Simply run cargo bench in that folder to run the micro-benchmarks.

We also have an implementaiton for js-framework-benchmark. Runnig this locally, however, is quite a bit more involved. Instead, the simplest way to run this is to add the "performance" label to your PR which will trigger the CI to run the benchmark automatically on every commit and post the result as a comment. Note, however, that the CI can be very noisy and that measurements should be taken with a grain of salt.