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
packagesarray of theworkspacesfield in thevets-websiteroot levelpackage.json. For example, to add thevaosapp 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.jsonto 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
workspacesfield of the root levelpackage.json, addsrc/platform/<the directory>to thepackagesarrayIn the same file and field, append any unique dependencies to the
"nohoist"array. For example, forpolyfillsdirectory:JSON"workspaces": { "packages":[ ... "src/platform/<the directory>", ... ], "nohoist": [ "**/platform-polyfills/classlist-polyfill" ] }Create
src/platform/<the directory>/exportsFile.jsin 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/platformdirectory: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-exportindirectly (through the
exportFilesintermediary).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/ExampleModulecan be shortened to./ExampleModuleadd all shared dependencies as
peerDependencieswith 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
dependenciesanddevDependeciesin thesrc/platform/<the directory>/package.jsonfile. List any unique dependencies in thenohoistfield in the rootpackage.json. See tip 1.Add all modules that are ok to be imported into a module outside of the current
src/platformdirectory as aliases in the export field of thepackage.jsonin the currentsrc/platformdirectory, 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_modulesto 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.jsonfile (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.jsonand 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-lockfileyou encounter this error:error Your lockfile needs to be updated, but yarn was run with '--frozen-lockfile'.Remove the followingresolutionsin 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.jsonand 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.