Victory is open to pull requests, issue reports, and questions from the community. Here are some good ways to get help if you need it.
- If you have a question, please open a new Q&A discussion thread
- If you think you have found a bug, open a new issue
If you are a new contributor looking to learn more about Victory, check out our good first issues board.
There are some parts of Victory that are in need of a little extra attention right now, including state management, transitions and animations, and pan and zoom behavior. These larger goals are being tracked in milestones. The goal for these milestones is re-work parts of Victory to make them more performant, easier to fix, and more accessible for new contributors. If your issue is related to one of these milestones, we may not be able to push up a fix right away, but we will label it accordingly and address it as a part of this larger scope of work.
Victory is a monorepo built with Wireit and pnpm workspaces. All victory-*
packages live in the packages
directory, and each has its own package.json
. Installing this repo with pnpm install
will automatically link all interdependent victory-*
packages. You must use pnpm
rather than npm
or yarn
when installing and running package.json
scripts in this project.
Clone this repo:
$ git clone https://github.com/FormidableLabs/victory.git
$ cd victory
Enable corepack (if not already):
$ corepack enable
Use pnpm to install dependencies:
$ pnpm install
We have some dev servers available for a sample development environment.
Note: The demo app is deprecated, all development should occur in storybook or the docs
# watch mode / HMR
$ pnpm storybook:dev
# storybook standalone
$ pnpm storybook:start
You can run the documentation website locally with the following command. It is linked to the Victory package via PNPM and will reflect changes in packages when they are rebuilt.
$ pnpm start:docs
Our task system mostly takes care of all task dependencies and things you need. When you first clone this repo or a new branch, run:
# Run all checks. Re-run this command for your normal workflow.
$ pnpm run check
# ... or add in a `--watch` to watch & re-run checks for only what you change!
$ pnpm run check --watch
# Build libraries and UMD distributions.
# Really only needed to double-check the webpack build still works.
$ pnpm run build
# ... or add in a `--watch` to watch & re-run the parts of the build that changed!
$ pnpm run build --watch
This will do all the build, seeding the task cache so subsequent tasks are fast, and checks that everything is correctly working. Your Victory workflow could reasonably just be (1) making some changes to files + tests, and then (2) re-running pnpm run check
!
Here are some other useful tasks (with or without a --watch
flag):
# Quality checks
$ pnpm run format
$ pnpm run format --watch
$ pnpm run lint
$ pnpm run lint --watch
$ pnpm run types:check
$ pnpm run types:check --watch
# Tests
$ pnpm run jest
$ pnpm run jest --watch
We also have some helper tasks to fix issues that are fixable.
$ pnpm run format:fix
$ pnpm run lint:fix
To develop against victory-native
, please see the package README.
For example, when working on a single package like victory-core
, you can run pnpm run check --watch
from the packages/victory-core
directory, and it will only check the core. Example:
$ cd packages/victory-core
$ pnpm run check --watch
This is especially helpful when you're making changes to any package that is depended upon, like victory-core
or victory-vendor
, and don't want to run every single script during development.
It seems like VS Code and WebStorm both struggle to update their internal cache, whenever the built types change. For example, when making changes to victory-core
, the TypeScript changes won't be picked up by your IDE automatically.
Instead of restarting your IDE completely, try restarting the TypeScript Service.
The initial build/check, or one where something that is part of a lot of cache keys changes, can really slow down your computer, especially if you've got an older model. To allow you to do other work on your computer at the same time, consider using the WIREIT_PARALLEL=<NUM_PROCESS>
environment variable like:
$ WIREIT_PARALLEL=4 pnpm run check
A good rubric is "number of cores" for max speed while still a mostly usable system or one less than that number for a much more usable system.
Wireit is a flexible tool that caches at the task level. So that means that out-of-the box any wireit task will run the entire task again if any of the input files
change. This leads us to two tips:
- Decompose tasks to package level: Jest could be run over the whole monorepo in one command, but we instead break it out per-package, so that we only re-run Jest tests for packages that have actually changed (or have dependencies that have changed).
- Use tool-specific caching: Tools like eslint and tsc can cache within a subtask run, so we like to leverage this within single tasks to make subtask re-runs faster.
The neat thing about wireit caching, is that for any high-level task, all of the sub-tasks that succeeded don't need to be re-run. So, if you're trying to run pnpm run check
and get a single package lint error, just fix that package lint error and run pnpm run check
again -- and then repeat until you get a pass! All of the work along the way that succeeds will be cached and won't be run again!
If you look at our package.json:scripts.start
command, you'll notice that we use both a wireit-based script (pnpm run build:lib:esm
) as well as a normal shell command (webpack serve ...
). This is a good example of the types of things that should and shouldn't be wireit script tasks.
- Wireit tasks: Tasks that should run on input file changes and then not run again should be wireit tasks. E.g. "transpile files", "lint files".
- Normal script tasks: Tasks that should always run regardless of the state of cache task execution. E.g. "start a webpack dev server".
We use tools caching within subtasks wherever we can. However, that can sometimes lead to weird errors. Here are some familiar ones with remedies.
Everything
If you want to make sure globally you're not hitting a cache issue, this command cleans the wireit cache as well as all the tool caches:
$ pnpm run clean:cache
Your next pnpm run build|check
will be a full (long) rebuild from scratch.
Eslint
If you hit something like:
/PATH/TO/victory/test/jest-setup.ts
0:0 error Parsing error: Debug Failure. False expression: /PATH/TO/victory/packages/victory-native/node_modules/victory-area/es/index.js linked to nonexistent file /PATH/TO/victory/packages/victory-area/es/index.js
✖ 1 problem (1 error, 0 warnings)
Then you've hit an eslint issue that can be fixed with:
$ pnpm run clean:cache:lint
Our task system is optimized for fast, easy developer experience, at the acknowledged cost of extra maintainer burden when we change task structure, add tasks, etc. If you are editing the scripts in a package.json
or package-scripts.js
you'll need to read up on Wireit and probably want to talk to an existing Victory maintainer.
We use three tools and some custom scripts as follows:
pnpm
torun
orexec
scriptswireit
to cache tasks and run dependent tasks.nps
to place one off script tasks in the root/project-scripts.js
in a manner that can be called from within a workspace.scripts/sync-pkgs-wireit*.js
: Scripts run withpnpm run sync
to automate dependency management. This is where most of your work for task management will take place.
For our packages, we primarily focus on four types of package.json files:
package.json
: Define root tasks here and aggregate workspace tasks here. Note that instead of relying onpnpm -r run
to concurrently run tasks in each workspace, we instead rely onwireit
alone to have dependencies in aggregate tasks on all subtasks. This is more efficient to havewireit
command concurrency and the task dependency graph.packages/victory*/package.json
: These package scripts are generated byscripts/sync-pkgs-wireit-helpers.js
. If you want to change them, edit that script and runpnpm run sync
.packages/victory-native/package.json
: A custom package.json that must have implementations or no-ops for all things invictory-core
.packages/victory-vendor/package.json
: A custom package.json that must have implementations or no-ops for all things invictory-core
.
Victory relies heavily on visual regression testing with Storybook and Chromatic.
Write visual tests for new features by adding them in the stories
directory. Run storybooks and check out changes. Storybooks are served from http://localhost:6006/
$ pnpm run storybook:server
This task also watches and rebuilds all Victory source files so you can more easily develop against storybook.
Chromatic provides automated visual testing. All internal PRs will trigger a new Chromatic build, which will be displayed along with CI status. Chromatic builds for Victory may be viewed in more detail here: https://www.chromaticqa.com/builds?appId=5b4acf7c54c0490024d5980b. Chromatic requires a secret app code to run, so PRs from external contributors will not automatically trigger a Chromatic build. For this reason, changes from external contributors will be checked out and opened as separate PRs so Chromatic may be used to verify any changes. Developers with access to the secret app code may also trigger a chromatic build manually with:
$ pnpm run chromatic
Note that Chromatic internally runs npm run build-storybook
around which we have a custom package.json:scripts.build-storybook
task that is meant to work within Chromatic.
We use changesets to create package versions and publish them.
Our official release path is to use automation to perform the actual publishing of our packages. The steps are to:
- A human developer adds a changeset. Ideally this is as a part of a PR that will have a version impact on a package.
- On merge of a PR our automation system opens a "Version Packages" PR.
- On merging the "Version Packages" PR, the automation system publishes the packages.
Here are more details:
When you would like to add a changeset (which creates a file indicating the type of change), in your branch/PR issue this command:
$ pnpm run changeset
to produce an interactive menu. Navigate the packages with arrow keys and hit <space>
to select 1+ packages. Hit <return>
when done. Select semver versions for packages and add appropriate messages. From there, you'll be prompted to enter a summary of the change. Some tips for this summary:
- Aim for a single line, 1+ sentences as appropriate.
- Include issue links in GH format (e.g.
#123
). - You don't need to reference the current pull request or whatnot, as that will be added later automatically.
After this, you'll see a new uncommitted file in .changesets
like:
$ git status
# ....
Untracked files:
(use "git add <file>..." to include in what will be committed)
.changeset/flimsy-pandas-marry.md
Review the file, make any necessary adjustments, and commit it to source. When we eventually do a package release, the changeset notes and version will be incorporated!
On a merge of a feature PR, the changesets GitHub action will open a new PR titled "Version Packages"
. This PR is automatically kept up to date with additional PRs with changesets. So, if you're not ready to publish yet, just keep merging feature PRs and then merge the version packages PR later.
On the merge of a version packages PR, the changesets GitHub action will publish the packages to npm.
For exceptional circumstances, here is a quick guide to manually publishing from a local computer using changesets.
-
Add a changeset with
pnpm run changeset
. Add changeset file, review file, tweak, and commit. -
Make a version. Due to our changelog plugin you will need to create a personal GitHub token and pass it to the environment.
$ GITHUB_TOKEN=<INSERT TOKEN> pnpm run version
Review git changes, tweak, and commit.
-
Publish.
First, build necessary files:
# Build everything $ pnpm run build
Then publish:
# Test things out first $ pnpm -r publish --dry-run # The real publish # This first does a single git tag (if not already present), then publishes $ pnpm run publish --otp=<insert otp code>
Note that publishing multiple packages via
changeset
to npm with an OTP code can often fail with429 Too Many Requests
rate limiting error. Take a 5+ minute coffee break, then come back and try again.Then issue the following to also push git tags:
$ git push && git push --tags