Yarn Classic (v1) Documentation

repository·master·Indexed 19 days ago

https://github.com/yarnpkg/website

Source code and documentation for the Yarn Classic (v1) website. Includes guides on using the Offline Mirror for local package installation, managing yarn.lock files for applications and libraries, project scaffolding with `yarn create`, and ensuring installation determinism.

Tokens
47.1K
Snippets
240
Records
290
Agent score
63%

What's inside Yarn Classic (v1) Website

  1. What is nohoist and when to use it

    master

    In a Yarn Workspaces monorepo, Yarn typically "hoists" dependencies to the project root to reduce redundancy. However, some libraries (like react-native) are incompatible with this scheme because they expect dependencies to be located in a local node_modules folder rather than a parent directory, or they fail to follow symlinks.

    nohoist is a mechanism that allows you to disable hoisting for specific modules. When a module is marked for nohoist, Yarn will place it and its dependencies within the actual child project's node_modules instead of the monorepo root, simulating a standalone project environment.

    Caution: Using nohoist can lead to duplicate modules in multiple locations, which increases disk usage and reduces the efficiency benefits of workspaces. Keep the nohoist scope as small and explicit as possible.

  2. What is the Yarn Offline Mirror?

    master

    The Offline Mirror is a feature that allows Yarn to install node_modules from local files on the file system instead of downloading them from a remote registry.

    Unlike a standard cache (which stores unzipped tarballs and may be implementation-specific), the Offline Mirror stores the original .tar.gz files. This makes it highly reliable for repeatable builds and allows the mirror to be consumed by any version of Yarn. It is particularly useful for ensuring builds work even when the network is unavailable or the registry is unreachable.

  3. What is Yarn Plug'n'Play (PnP)?

    master

    Plug'n'Play (PnP) is an alternative installation strategy for Yarn that replaces the traditional node_modules directory. Instead of generating a massive directory of files for Node to traverse via filesystem lookups, PnP generates a single .pnp.js file. This file acts as a map that tells Yarn and Node exactly where every package is located, allowing for faster installations and more efficient runtime resolution.

    Key benefits of PnP include:

    • Faster Installs: Eliminates the I/O-heavy process of generating the node_modules directory, which can account for over 70% of yarn install time.
    • Strict Dependency Management: Prevents "phantom dependencies" (using a package that is available in node_modules but not explicitly listed in your package.json) by ensuring Node only accesses packages Yarn has explicitly authorized.
    • Improved Runtime Performance: Reduces the number of stat and readdir syscalls Node must perform to resolve modules, leading to faster application boot times.
    • Efficient Deduplication: Allows for better package deduplication and sharing across projects compared to the nested structure of node_modules.
  4. Use version lifecycle methods and environment variables

    master

    The yarn version command triggers a specific lifecycle sequence. You can hook into this process by defining scripts in your package.json:

    1. preversion: Runs before the version is bumped.
    2. version: The core versioning command.
    3. postversion: Runs after the version is bumped.

    Environment Variables

    During these scripts, Yarn provides environment variables to access version information:

    • $npm_package_version in preversion: Holds the version before the change.
    • $npm_package_version in postversion: Holds the version after the change.

    This is useful for automating tasks like running tests before a release or pushing tags and publishing to a registry after a release.

    {
      "name": "example-yarn-package",
      "version": "1.0.2",
      "scripts": {
        "test": "echo "Running tests for version $npm_package_version..."",
        "preversion": "yarn test",
        "postversion": "git push --tags && yarn publish . --tag $npm_package_version && git push && echo "Successfully released version $npm_package_version!""
      }
    }
  5. Extract a complete dependency tree from a root dependency

    master

    To build a dependency tree in memory before persisting it to the filesystem, you can use a recursive function that resolves volatile references (like semver ranges) into pinned references (specific versions) and then fetches their sub-dependencies.

    This approach allows for manipulations like deduplication or hoisting to be applied to the in-memory tree rather than the disk, which is significantly faster.

    Handling Circular Dependencies

    When extracting trees, circular dependencies (e.g., babel-core $\rightarrow$ babel-register $\rightarrow$ babel-core) can cause infinite recursion and memory exhaustion. To prevent this, implement a filtering pass that checks if a dependency is already satisfied by a package available in the upstream dependency chain (the available registry).

    If a dependency's reference matches an available reference, or if the available reference satisfies the dependency's semver range, skip the resolution for that branch.

    // A robust implementation of dependency tree extraction
    async function getPackageDependencyTree(
      { name, reference, dependencies },
      available = new Map()
    ) {
      return {
        name,
        reference,
        dependencies: await Promise.all(
          dependencies
            .filter(volatileDependency => {
              let availableReference = available.get(volatileDependency.name);
    
              // Skip if the reference exactly matches an available package
              if (volatileDependency.reference === availableReference) return false;
    
              // Skip if the available package satisfies the semver range
              if (
                semver.validRange(volatileDependency.reference) &&
                semver.satisfies(availableReference, volatileDependency.reference)
              ) {
                return false;
              }
    
              return true;
            })
            .map(async volatileDependency => {
              let pinnedDependency = await getPinnedReference(volatileDependency);
              let subDependencies = await getPackageDependencies(pinnedDependency);
    
              // Pass down the registry of available packages to the next level
              let subAvailable = new Map(available);
              subAvailable.set(pinnedDependency.name, pinnedDependency.reference);
    
              return await getPackageDependencyTree(
                Object.assign({}, pinnedDependency, { 
                  dependencies: subDependencies 
                }),
                subAvailable
              );
            })
        ),
      };
    }
  6. Understand Semantic Versioning (semver)

    master

    Yarn packages follow Semantic Versioning (semver), which uses a major.minor.patch format (e.g., 3.14.1). This system communicates the nature of changes in a package:

    • Major: Incremented for breaking or incompatible API changes.
    • Minor: Incremented for new functionality that is backwards-compatible.
    • Patch: Incremented for bug fixes that are backwards-compatible.

    When versions are described as "compatible," it refers to changes in the minor and patch segments.

  7. Distinguish between Runtime and Development dependencies

    master

    When managing dependencies in Yarn, understand the two primary categories:

    • Runtime Dependencies: Required by the project's code to function when the application is running. These are installed when a library is consumed by a user.
    • Development Dependencies: Only required for working directly on the project (e.g., testing frameworks, build tools, linters). These are not installed when a library is consumed by a user; they are only installed when working within the project itself.

    Note: Most projects have a significantly larger tree of development dependencies than runtime dependencies. Because this tree is larger, it is more susceptible to breaking changes from third-party updates.

  8. Map CLI arguments to `.yarnrc` settings

    master

    You can configure CLI flags in a .yarnrc file by using the syntax --<command>.<flag> <value>. This is equivalent to running the command with that flag in the terminal.

    Examples:

    Setting --install.check-files true in .yarnrc is equivalent to running:

    yarn install --check-files

    Setting --cache-folder /tmp/yarn-cache/ in .yarnrc is equivalent to running:

    yarn cache dir
    # Output will reflect the configured path
    # Example .yarnrc content
    --install.check-files true
    --cache-folder /tmp/yarn-cache/
  9. Use `pre` and `post` script prefixes

    master

    Yarn automatically supports lifecycle hooks by prefixing script names with pre or post. If you define a script named pre<name>, it will execute automatically before the <name> script is run.

    For example, if you have a build script and a prebuild script, running yarn run build will execute prebuild first, followed by build.

    {
      "name": "my-package",
      "scripts": {
        "build": "babel src -d lib",
        "prebuild": "jest"
      }
    }
  10. What are distribution tags (dist-tags)?

    master

    Distribution tags (or dist-tags) are labels applied to specific published versions of a package. They allow users to install a package using a label instead of a specific semver version number.

    Commonly used tags include:

    • latest: The current version of the package. This is the only tag with special meaning; it is used to determine which version to install when no version is specified.
    • stable: Typically the latest stable release.
    • beta: Used for upcoming changes before they reach latest or stable.
    • canary: A "nightly" or pre-beta release for very early code.
    • dev: Used for testing a single revision through the registry.

    Note that projects can define custom tags (e.g., next), but latest is the only one that affects default installation behavior.

  11. How Pre-release tags affect version matching

    master

    Versions with pre-release tags (e.g., 3.1.4-beta.2) have strict matching rules. If a comparator includes a version with a pre-release tag, it will only match against other versions that share the exact same major.minor.patch version.

    For example, the range >=3.1.4-beta.2 will match 3.1.4-beta.12, but it will not match 3.1.5-beta.1, even though 3.1.5-beta.1 is technically a higher version.