Skip to content

pnpm

PnpmSetupSummary

Bases: BaseModel

Summary of the pnpm setup operation.

Attributes:

Name Type Description
node_modules str

...

package str

...

npmrc str

...

pnpm_lock str | None

...

Source code in m/devcontainer/pnpm.py
class PnpmSetupSummary(BaseModel):
    """Summary of the pnpm setup operation."""

    node_modules: str
    package: str
    npmrc: str
    pnpm_lock: str | None

Create a symlink that is linked to a source.

This is a destructive operation - if the link exists it will be removed.

Parameters:

Name Type Description Default
link Path

The path to the link.

required
source Path

The path to the source.

required
Source code in m/devcontainer/pnpm.py
def create_symlink(link: Path, source: Path) -> None:
    """Create a symlink that is linked to a source.

    This is a destructive operation - if the link exists it will be removed.

    Args:
        link: The path to the link.
        source: The path to the source.
    """
    if link.exists() or link.is_symlink():
        link.unlink()
    link.symlink_to(source)

pnpm_setup(work_dir, pnpm_dir)

Create symbolic links to a mounted volume.

This is done so that pnpm may take advantage of the cache associated with a single pnpm store. When we execute pnpm install in the devcontainer we want to make sure that other containers may be able to share the pnpm cache. To do this we need all containers to use the same pnpm store.

Parameters:

Name Type Description Default
work_dir str

The directory where the project is mounted.

required
pnpm_dir str

The directory where the project will execute pnpm commands.

required

Returns:

Type Description
Res[None]

A PnpmSetupSummary instance.

Source code in m/devcontainer/pnpm.py
def pnpm_setup(work_dir: str, pnpm_dir: str) -> Res[None]:
    """Create symbolic links to a mounted volume.

    This is done so that pnpm may take advantage of the cache associated with
    a single pnpm store. When we execute `pnpm install` in the devcontainer
    we want to make sure that other containers may be able to share the pnpm
    cache. To do this we need all containers to use the same pnpm store.

    Args:
        work_dir: The directory where the project is mounted.
        pnpm_dir: The directory where the project will execute pnpm commands.

    Returns:
        A `PnpmSetupSummary` instance.
    """
    package_res = _setup_package(work_dir, pnpm_dir)
    if isinstance(package_res, Bad):
        return Bad(package_res.value)
    package_summary = package_res.value

    node_modules_res = _setup_node_modules(work_dir, pnpm_dir)
    if isinstance(node_modules_res, Bad):
        return Bad(node_modules_res.value)
    node_modules_summary = node_modules_res.value

    npmrc_res = _setup_npmrc(work_dir, pnpm_dir)
    if isinstance(npmrc_res, Bad):
        return Bad(npmrc_res.value)
    npmrc_summary = npmrc_res.value

    # perform a few checks with the lock file
    work_lock = Path(work_dir) / 'pnpm-lock.yaml'
    pnpm_lock_summary = None
    if not work_lock.exists():
        pnpm_lock_summary = f'MISSING {work_lock}'
        logger.warning('pnpm_lock_missing', context={
            'work_lock': pnpm_lock_summary,
            SUGGESTION: 'run `pnpm install` to generate the lock file',
        })
    if work_lock.is_symlink():
        pnpm_lock_summary = f'unlinked symlink {work_lock}'
        work_lock.unlink()

    summary = PnpmSetupSummary(
        node_modules=node_modules_summary,
        package=package_summary,
        npmrc=npmrc_summary,
        pnpm_lock=pnpm_lock_summary,
    )
    logger.debug('pnpm_setup_summary', context=summary.model_dump())
    return Good(None)

run_pnpm(pnpm_args, *, force_cd)

Execute the pnpm command with the given arguments.

Parameters:

Name Type Description Default
pnpm_args list[str]

The arguments to pass to pnpm.

required
force_cd bool

If True, change the working directory to the mounted volume.

required

Returns:

Type Description
Res[None]

The exit code of the pnpm command.

Source code in m/devcontainer/pnpm.py
def run_pnpm(pnpm_args: list[str], *, force_cd: bool) -> Res[None]:
    """Execute the pnpm command with the given arguments.

    Args:
        pnpm_args: The arguments to pass to pnpm.
        force_cd: If True, change the working directory to the mounted volume.

    Returns:
        The exit code of the pnpm command.
    """
    workdir = str(Path.cwd())
    workspace_res = _get_workspaces(workdir)
    if isinstance(workspace_res, Bad):
        return Bad(workspace_res.value)

    workspace, pnpm_workspace = workspace_res.value
    pnpm_command = pnpm_args[0] if pnpm_args else None
    is_cd_command = pnpm_command in PNPM_MOUNTED_COMMANDS
    if not force_cd and (not pnpm_command or not is_cd_command):
        pnpm_res = sub.exec_pnpm(pnpm_args)
        if isinstance(pnpm_res, Bad):
            return pnpm_res
        return Good(None)

    if not Path(f'{workdir}/package.json').exists():
        return issue('missing_package_json', context={
            'workdir': workdir,
            'warning': 'run pnpm commands in directories with package.json',
        })
    pnpm_dir = workdir.replace(workspace, pnpm_workspace, 1)
    return _pnpm(workdir, pnpm_dir, pnpm_args)