Skip to main content

Install dependencies

jko allows you to install dependencies and devDependencies.

$ jko install

It will install dependencies from jko.js or package.json.

info

jko.js takes precedence over package.json.


Install dependencies from a File

$ jko install --config-file=./path/to/config.js

or

$ jko install -c=./path/to/config.js

Install dependencies from a Package

$ jko install --config-file=somePackageName

or

$ jko install -c=somePackageName

The package must be available, i.e. It must be installed first.


Using a Specific Package Manager to Install Dependencies

jko supports the following package managers:

  • npm (Default)
  • pnpm
  • yarn
$ jko install --package-manager=npm

or

$ jko install --p=npm

or by using the jko.js file (package.json or any other file specified with the --config-file option):

export default {
dependencies: {
packageName1: "#.#.#",
packageNameN: "#.#.#"
},
devDependencies: {
devPackageName1: "#.#.#",
devPackageNameN: "#.#.#"
},
packageManager: 'npm',
}
$ jko install

How Are Dependencies Installed

  1. Before installation begins, $pre (install) is run if it exists.
  2. Dependencies are installed as follows:
  • Using a non-package.json file (e.g. jko.js): Each dependency is processed independently using the selected package manager command. For example:

    • npm install some0@"4.0.0" && npm install -D some1@"5.0.0" some2@"6.0.0"
    • pnpm add some1@"5.0.0" some2@"6.0.0"

    Additionally, if a package.json file does not exist, it will be created; otherwise, it will be updated.

  • Using package.json for installation: All dependencies are processed at once (e.g. by running npm install or yarn install).

  1. If everything completes successfully, $post (install) is run if it exists. If an error occurs, $catch (install) is run if defined.
warning

When installing from a non-package.json file, the packageManager doesn't "lock" its version to a fixed package in your lock file. Instead, it treats the package as a new dependency and updates the lock file accordingly.

What happens is:

  1. The packageManager determines which version of the package to install based on the semver range (defaulting to the latest version if none is provided).
  2. It downloads that version and either adds a new entry or updates the existing one in package.json.
  3. It deletes any packages that have been removed from package.json.
  4. It updates or creates package-lock.json with the resolved version.

In this process, the package-lock file isn't "enforcing" a previously locked version for the dependency—It is just updated with the new resolution if any changes occur.

While this approach is often beneficial, it also means you need to manage your semver ranges carefully. For instance, to pin a version, use some@">=1.50.0 && <1.51.0". This guarantees that only version 1.50.x will be installed (with x representing bug fixes), which can be advantageous.

danger

If an install task already exists, jko completely ignores it.


How Dependencies Are Deleted

There are two scenarios to consider:

  1. Managing dependencies solely via package.json.
  2. Using a custom configuration file, e.g. jko.js.

Using only package.json

By relying exclusively on package.json as the single source of truth for dependencies, you lose the ability to import shared dependencies definition. When you remove a dependency from package.json and run jko install, the specified dependency is removed as expected.

Using a custom configuration file

When dependencies are defined in both package.json and a shared configuration file, e.g. jko.js, you have multiple sources of truth. Consequently, removing a dependency requires updating both locations:

  • Remove the dependency from package.json.
  • Remove the dependency from the shared configuration file, e.g. jko.js.
note

This occurs because when you define shared dependencies in a file like jko.js, running jko install will always update package.json via the package manager behind the scenes, resulting in at least two sources of truth.

Example:

jko.js

export default {
dependencies: {
packageName1: "#.#.#",
packageNameN: "#.#.#"
},
devDependencies: {
devPackageName1: "#.#.#",
devPackageNameN: "#.#.#"
},
packageManager: 'npm',
}

When you run jko install, the package.json file is updated to:

{
"dependencies": {
"packageName1": "#.#.#",
"packageNameN": "#.#.#"
},
"devDependencies": {
"devPackageName1": "#.#.#",
"devPackageNameN": "#.#.#"
},
}

To remove "packageName1": "#.#.#", delete it from both jko.js and package.json:

jko.js

export default {
dependencies: {
packageNameN: "#.#.#"
},
devDependencies: {
devPackageName1: "#.#.#",
devPackageNameN: "#.#.#"
},
packageManager: 'npm',
}

package.json:

{
"dependencies": {
"packageNameN": "#.#.#"
},
"devDependencies": {
"devPackageName1": "#.#.#",
"devPackageNameN": "#.#.#"
},
}

Then, you can run jko install.

danger

If a dependency is defined in multiple files, it must be removed from each one to ensure it’s completely deleted. For example:

jko.js

import dependencies1 from 'somePackage'
import dependencies2 from './moreDependencies.js'

export default {
dependencies: {
...dependencies1
...dependencies2
}
}

Then remove the package from both dependencies1 and dependencies2 before running jko install, or remove it prior to exporting:

jko.js

import dependencies1 from 'somePackage'
import dependencies2 from './moreDependencies.js'

const dependencies = {
...dependencies1
...dependencies2
}

delete dependencies."some package"

export default {
dependencies
}

Mixing jko.js and JSON

If you want to use jko.js and store dependencies in a JSON file (e.g., package.json), import JSON file into jko.js:

jko.js

import scripts from 'somePackage'
import packageJson from './package.json' with { type: 'json' }

export default {
...packageJson,
scripts: {
...scripts.scripts,
yourScript1: "someCommand1",
yourScript2: "someCommand2",
}
}

This is especially useful when using shared scripts from a local package.