Skip to content

Latest commit

 

History

History
59 lines (42 loc) · 1.73 KB

yarnPnpInterop.md

File metadata and controls

59 lines (42 loc) · 1.73 KB

Yarn PnP interop

Asked about it here: https://discord.com/channels/226791405589233664/654372321225605128/957301175609344070

PnP API checks if import specifiers are for dependencies: non-relative, non-absolute libfoo @scope/libfoo

When they are, it does resolveToUnqualified to map to an unqualified path. This path points to the module's location on disk (in a zip, perhaps) but does not handle file extension resolution or stuff like that.

To interop with PnP, we need PnP to only resolveToUnqualified. We do everything else.

import { Module } from 'module';
import fs from 'fs';

const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:@[^/]+\/)?[^/]+)\/*(.*|)$/;

const originalModuleResolveFilename = Module._resolveFilename;
Module._resolveFilename = function (
    request: string,
    parent: typeof Module | null | undefined,
    isMain: boolean,
    options?: { [key: string]: any }
) {
    const dependencyNameMatch = request.match(pathRegExp);
    if (dependencyNameMatch !== null) {

      const [, dependencyName, subPath] = dependencyNameMatch;

      const unqualified = pnpapi.resolveToUnqualified(....);

      // Do your modified resolution on the unqualified path here

    } else {

      // Do your modified resolution here; no need for PnP

    }

};

PnP can be installed at runtime.

To conditionally check if PnP is available at the start of every resolution:

// Get the pnpapi of either the issuer or the specifier.
// The latter is required when the specifier is an absolute path to a
// zip file and the issuer doesn't belong to a pnpapi
const {findPnPApi} = Module;
const pnpapi = findPnPApi ? (findPnpApi(issuer) ?? (url ? findPnpApi(specifier) : null)) : null;
if (pnpapi) {...}