Worktree and Workspace Setup Guide
This document is a guide for VFS engineers who are tasked with adding Yarn Workspaces to vets-website src/platform/*
and src/applications/*
Setting up the worktree and apps' workspaces
If an application is on the allowlist, it can be converted to a Yarn workspace. To do so:
add an entry for your app in the
packages
array of theworkspaces
field in thevets-website
root levelpackage.json
. For example, to add thevaos
app you would do the following:CODE"workspaces": { "packages": [ "src/applications/vaos" ] }
Create a list of your application’s dependencies. Separate this list into two categories: dependencies that are shared by other applications (shared dependencies) and dependencies that only your application uses (isolated dependencies).
Add a
package.json
to the root level of your application. To thispackage.json
:set the
"name"
property to@department-of-veterans-affairs/applications-{my-app-name}
add all shared dependencies to
peerDependencies
. For each dependency here, set its version to"*"
, e.g.CODE"peerDependencies": { "react": "*" }
The point here is that if the version of the dependency is changed at the vets-website
root level package.json
, then your app will use that version.
b. add any isolated dependencies to the dependencies
or devDependencies
in the app-level package.json
, as appropriate.
c. for each isolated dependency add an entry to the no-hoist
array in the vets-website
root level package.json
. For example, if your application were called my-app
and you had an isolated dependency of cowsay
then you would do the following:
"workspaces": {
"packages": [
"src/applications/my-app"
],
"nohoist": [
"**/applications-my-app/cowsay"
]
}
After this step, when you do a yarn install
you will see a node_modules
folder inside applications/my-app
and it will contain cowsay
.
Add any app-level scripts you wish to the
scripts
section of the app-level package.json
src/platform
code workspaces:
In the
workspaces
field of the root levelpackage.json
, addsrc/platform/<the directory>
to thepackages
arrayIn the same file and field, append any unique dependencies to the
"nohoist"
array. For example, forpolyfills
directory:JSON"workspaces": { "packages":[ ... "src/platform/<the directory>", ... ], "nohoist": [ "**/platform-polyfills/classlist-polyfill" ] }
Create
src/platform/<the directory>/exportsFile.js
in this file import all modules that we want to be made available to export from among all the modules in the directory
re-export them; doing this allows us to have two ways to import a module from a
src/platform
directory:directly (which requires knowing the “alias” to a module). For example, with the above package.json a user would do:
import polyfillModule from @department-of-veterans-affairs/platform-polyfill/some-module-that-is-ok-to-export
indirectly (through the
exportFiles
intermediary).import { polyfillModule } from @department-of-veterans-affairs/platform-polyfill
In
src/platform/<the directory>
, create apackage.json
. Here is an example forpolyfills
:CODE{ "name": "@department-of-veterans-affairs/platform-<the directory>", "version": "1.0.0", "exports": { "." :"./exportsFile.js", "./some-module-that-is-ok-to-export" :"./path-to-some-module-that-is-ok-to-export" }, peerDependencies:{ ... }, "license": "MIT", "private": true }
export keys should resemble the directory and file name of the export. Common names can be excluded from the name if there are no conflicts ie:
./components/ExampleModule
can be shortened to./ExampleModule
add all shared dependencies as
peerDependencies
with version as"*"
This way, if a dependency at the root level changes there will not be a mis-match with the version in the workspace.
List
dependencies
anddevDependecies
in thesrc/platform/<the directory>/package.json
file. List any unique dependencies in thenohoist
field in the rootpackage.json
. See tip 1.Add all modules that are ok to be imported into a module outside of the current
src/platform
directory as aliases in the export field of thepackage.json
in the currentsrc/platform
directory, e.g."./some-module-that-is-ok-export"
We are going to be encouraging application teams to import modules into their apps via yarn workspace syntax
e.g.
import FEATURE_FLAG_NAMES from '@department-of-veterans-affairs/platform-utilities/featureFlagNames'
instead ofimport FEATURE_FLAG_NAMES from 'platform/utilities/feature-toggles/featureFlagNames'.
When importing nested modules from a yarn workspace (i.e. modules inside a folder inside the workspace) ESlint will not be able to resolve the module specified by the new yarn workspace syntax unless an alias for the module is added to the root level
babel.config.json
.for the above import the alias added is
"@department-of-veterans-affairs/platform-utilities/featureFlagNames": "./src/platform/utilities/feature-toggles/featureFlagNames.js"
(note the extension is included).
Testing
Delete all pertinent node_modules folders
Run yarn install
Check the root
node_modules/@department-of-veterans-affairs/platform-<the directory>
existCheck
<the directory>
rootnode_modules
to make sure non hoisted dependencies are present and correctRun yarn watch to test for compilation and runtime errors; the yarn workspaces installation should NOT affect the import/export running of current code (only provide a new option)
Run
src/platform/*
unit tests withyarn test:unit src/platform/**/*.unit.spec.js
[Test imports from new
@department-of-veterans-affairs/...
] (note we are still having issues with Babel)
Trouble shooting notes
In the root package.json
file engines.yarn
must be set to “1.19.1"
, this is to avoid a known bug which prevents yarn workspace <workspace-name> add <dependency>
from working
Tips
You can get list of imported dependencies ( with repo level install information) by running
node script/list-imports.js --app-folder [<the directory]
inside a workspace, ESlint will not be able to resolve an import from another workspace if of the following form:
CODE// in a file in platform/forms import FormApp from 'platform/forms-system/src/js/containers/FormApp';
The solution to this problem was to add the following to the root level .eslintrc.js
in vets-website
:
settings: {
'import/resolver': {
node: {
moduleDirectory: ['node_modules', 'src/'],
},
'babel-module': {},
},
},
Debugging
If you are running into a LARGE number of compiler errors, first check that the root directory contains a
babel.config.json
file (as opposed to.babelrc
)If you run into importing errors, check that your package.json file does NOT have the “type” field.
if you add an alias to
babel.config.json
and are still getting an ESlint error that the module can’t be resolved try restarting VS Code.if you are working on the worktree set up, and when running (or when CI runs)
yarn install --frozen-lockfile
you encounter this error:error Your lockfile needs to be updated, but yarn was run with '--frozen-lockfile'.
Remove the followingresolutions
in the rootpackage.json
:"**/yargs-parser": "13.1.2", "punycode": "1.4.1", "**/moment": "^2.29.2"
Note that, alternatively you can run
yarn install --pure-lockfile
.If you get a linting error when importing from aliases that start with: ‘@@’ or '~', find the alias for the import in
babel.config.json
and use the original path. Workspaces does not work well with Babel when it comes to recognizing un-standard paths.If the
yarn workspace <workspace-name> add <depency-name>
command is failing, check that yarn engine being used is 1.19.1
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.