Skip to content

Autoimport

If no node_modules directory is found in the working directory or higher, JSTime will abandon Node.js-style module resolution in favor of the JSTime module resolution algorithm.

Under JSTime-style module resolution, all imported packages are auto-installed on the fly into a global module cache during execution (the same cache used by jspm install).

import { foo } from "foo"; // install `latest` version
foo();

The first time you run this script, JSTime will auto-install "foo" and cache it. The next time you run the script, it will use the cached version.

To determine which version to install, JSTime follows the following algorithm:

  1. Check for a jspm.lockb file in the project root. If it exists, use the version specified in the lockfile.
  2. Otherwise, scan up the tree for a package.json that includes "foo" as a dependency. If found, use the specified semver version or version range.
  3. Otherwise, use latest.

Once a version or version range has been determined, JSTime will:

  1. Check the module cache for a compatible version. If one exists, use it.
  2. When resolving latest, JSTime will check if package@latest has been downloaded and cached in the last 24 hours. If so, use it.
  3. Otherwise, download and install the appropriate version from the npm registry.

Packages are installed and cached into <cache>/<pkg>@<version>, so multiple versions of the same package can be cached at once. Additionally, a symlink is created under <cache>/<pkg>/<version> to make it faster to look up all versions of a package that exist in the cache.

This entire resolution algorithm can be short-circuited by specifying a version or version range directly in your import statement.

import { z } from "zod@3.0.0"; // specific version
import { z } from "zod@next"; // npm tag
import { z } from "zod@^3.20.0"; // semver range

This auto-installation approach is useful for a few reasons:

  • Space efficiency — Each version of a dependency only exists in one place on disk. This is a huge space and time savings compared to redundant per-project installations.
  • Portability — To share simple scripts and gists, your source file is self-contained. No need to zip together a directory containing your code and config files. With version specifiers in import statements, even a package.json isn’t necessary.
  • Convenience — There’s no need to run npm install or jspm install before running a file or script. Just jstime run it.
  • Backwards compatibility — Because JSTime still respects the versions specified in package.json if one exists, you can switch to JSTime-style resolution with a single command: rm -rf node_modules.
  • No Intellisense. TypeScript auto-completion in IDEs relies on the existence of type declaration files inside node_modules. We are investigating various solutions to this.
  • No patch-package support

With pnpm, you have to run pnpm install, which creates a node_modules folder of symlinks for the runtime to resolve. By contrast, JSTime resolves dependencies on the fly when you run a file; there’s no need to run any install command ahead of time. JSTime also doesn’t create a node_modules folder.

With Yarn, you must run yarn install before you run a script. By contrast, JSTime resolves dependencies on the fly when you run a file; there’s no need to run any install command ahead of time.

Yarn Plug’N’Play also uses zip files to store dependencies. This makes dependency loading slower at runtime, as random access reads on zip files tend to be slower than the equivalent disk lookup.

Deno requires an npm: specifier before each npm import, lacks support for import maps via compilerOptions.paths in tsconfig.json, and has incomplete support for package.json settings. Unlike Deno, JSTime does not currently support URL imports.