This Yarn plugin adds support for the `condition:` protocol. It can be used for conditionally selecting a dependency version depending on a specific flag.
MIT License
@yarnpkg/plugin-conditions
This Yarn 4 plugin adds support for the condition:
protocol. It can be used for conditionally selecting a dependency version depending on a specific flag. Additionally, it can be use to optionally include or exclude some package.json
fields using the new "conditions"
field.
Conditions are toggled based on a corresponding environment flag.
⚠️ Only CommonJS dependencies are supported at the moment.
You can add this plugin to your Yarn 4 ("Yarn Berry") project running the following command:
yarn plugin import https://raw.githubusercontent.com/nicolo-ribaudo/yarn-plugin-conditions/main/bundles/%40yarnpkg/plugin-conditions.js
Before using conditions, you must first list the allowed ones in your .yarnrc.yml
file. You can optionally specify a default value for the condition when it's not explicitly set.
# .yarnrc.yml
conditions:
node10:
default: true
experimental: {}
This plugin will throw an error if you use a condition not defined in your configuration: this is because a typo in the condition name will result in a package published with the wrong dependencies.
You can now use the condition:
protocol to specify dependency versions. It's syntax is
condition: <test> ? <if-true> : <if-false>
test
is the name of an environment variableif-true
is the dependency version to use when <test>
is true
(optional).if-false
is the dependency version to use when <test>
is true
(optional).When running yarn install
, Yarn will install both the <if-true>
and <if-false>
dependencies. At runtime, it will decide which dependency to load based on the <test>
condition.
For ESM dependencies, you have to explicitly specify the list of exports you want to be re-exported:
condition: <test> ? <if-true> : <if-false> (esm:var1|default|anotherExport)
If you need the intermediate package to have peerDependency
so that they can be properly propagate to the <if-true>
and <if-false>
packages, you need to explicitly list them:
condition: <test> ? <if-true> : <if-false> (peer:pkg-1|@another/package)
You can also add a "conditions"
field to your package.json
file to specify which fields to use when a specific condition is enabled:
{
// ...
"conditions": {
"<test>": [{ /* <if-true> */ }, { /* <if-false> */ }]
}
}
When running yarn pack
or yarn publish
, Yarn will remove the condition:
protocol and the "conditions"
field from package.json
files, replacing it with the dependency version selected by the <test>
env variable. This makes sure that packages published on the npm registry are compatible with the rest of the ecosystem.
If you want to remove the conditions from your package.json
files, you can run the yarn condition materialize <test> [--true|--false]
command.
💡 Since the
"conditions"
field is only resolved when packing/publishing, its contents won't affect the Node.js behavior during development. If necessary, you can specify the conditional fields outside of the"conditions"
field, with a value that works for the different conditions test values.
condition:
protocolYou could set your dependencies like this:
{
"dependencies": {
"chokidar": "condition: node10 ? ^3.5.1 : ^2.1.8",
"rollup": "condition: experimental ? : ^2.36.2",
"parcel": "condition: experimental ? next : "
}
}
This can be resolved to four different sets of dependencies:
node10
is true
or unset, experimental
is false
or unset:
{
"dependencies": {
"chokidar": "^3.5.1",
"rollup": "^2.36.2"
}
}
node10
is true
or unset, experimental
is true
:
{
"dependencies": {
"chokidar": "^3.5.1",
"parcel": "next"
}
}
node10
is false
, experimental
is false
or unset:
{
"dependencies": {
"chokidar": "^2.1.8",
"rollup": "^2.36.2"
}
}
node10
is false
, experimental
is true
:
{
"dependencies": {
"chokidar": "^2.1.8",
"parcel": "next"
}
}
If you want to remove the experimental
condition (evaluating it to true
) you can run
yarn condition materialize experimental --true
The package.json
file will be automatically updated to
{
"dependencies": {
"chokidar": "condition: node10 ? ^3.5.1 : ^2.1.8",
"parcel": "next"
}
}
"conditions"
fieldYou might want to rename an entry-point of your package, from ./old-name
to ./new-name
, but only do it when a given condition is enabled:
{
"conditions": {
"experimental": [ /* if-true */{
"exports": {
"./new-name": "./lib/new-name.js"
}
}, /* if-false */ {
"exports": {
"./old-name": "./lib/old-name.js"
}
}]
},
"exports": {
"./old-name": "./lib/old-name.js",
"./new-name": "./lib/new-name.js"
}
}
With this package.json
both ./old-name
and ./new-name
will work during development, but the published package.json
will be either
{
"exports": {
"./old-name": "./lib/old-name.js"
}
}
or
{
"exports": {
"./new-name": "./lib/new-name.js"
}
}
Contributions are welcome! The most lacking part now is tests (I haven't set them up yet).
Steps:
yarn
to install its dependencies.source
directory.yarn build
every time you update the source
folder. Don't forget to commit the result!You can test your changes by creating a separate Yarn project, and importing this plugin by adding the following options to the .yarnrc.yml
file of that project:
plugins:
- path: <path-to-this-plugin-folder>/bundles/@yarnpkg/plugin-conditions.js
spec: <path-to-this-plugin-folder>/bundles/@yarnpkg/plugin-conditions.js