rushstack icon indicating copy to clipboard operation
rushstack copied to clipboard

[api-extractor] Yarn PnP - "ENOENT: package.json not found"

Open sammagee opened this issue 2 years ago • 5 comments

Summary

When attempting to use api-extractor or api-extractor-model, I run into the issue stated in the title. I am working within a yarn/PnP monorepo. I have observed that this is related to the Extractor and ApiPackage (respectively) classes attempting to resolve their own package.json for JSON metadata.

Repro steps

  1. Create a monorepo with yarn workspaces and PnP set up. (Perhaps using something like this would be helpful)
  2. Attempt to run api-extractor, either from the command line or programmatically.

Expected result: Successful JSON generation.

Actual result:

/home/user/<monorepo>/.pnp.cjs:720418
  return Object.assign(new Error(`${code}: ${message}`), { code });
                       ^

Error: ENOENT: no such file or directory, lstat '/node_modules/@microsoft/api-extractor/lib/api/package.json'
    at makeError$1 (/home/user/<monorepo>/.pnp.cjs:720418:24)
    at ENOENT (/home/user/<monorepo>/.pnp.cjs:720433:10)
    at ZipFS.realpathSync (/home/user/<monorepo>/.pnp.cjs:721811:13)
    at /home/user/<monorepo>/.pnp.cjs:723115:100
    at /home/user/<monorepo>/.pnp.cjs:723611:60
    at ZipOpenFS.getZipSync (/home/user/<monorepo>/.pnp.cjs:723740:14)
    at ZipOpenFS.makeCallSync (/home/user/<monorepo>/.pnp.cjs:723611:17)
    at ZipOpenFS.realpathSync (/home/user/<monorepo>/.pnp.cjs:723107:17)
    at VirtualFS.realpathSync (/home/user/<monorepo>/.pnp.cjs:722857:26)
    at PosixFS.realpathSync (/home/user/<monorepo>/.pnp.cjs:722608:41) {
  code: 'ENOENT'
}

Node.js v18.15.0

Details

As stated in the summary, I believe this is due to the Extractor and ApiPackage classes attempting to load the package.json from their own packages and failing, because of the nature of "Plug'n'Play". I am able to get around the issue by either:

  • using yarn unplug @microsoft/api-extractor && yarn unplug @microsoft/api-extractor-model (not desired, adds to the install time and disk space)
  • using yarn patch @microsoft/api-extractor && yarn patch @microsoft/api-extractor-model then overriding the areas that look up the package.json

I have gone with the second option for now, as it keeps the scope to the package I am working on, but would prefer to do neither, as they both have implications for manually upgrading the packages.

Standard questions

Please answer these questions to help us investigate your issue more quickly:

Question Answer
@microsoft/api-extractor version? 7.35.1
@microsoft/api-extractor-model version? 7.27.1
Operating system? Linux
API Extractor scenario? docs (.api.json)
Would you consider contributing a PR? Yes
TypeScript compiler version? 5.0.4
Node.js version (node -v)? 18.15.0

sammagee avatar Jun 02 '23 15:06 sammagee

FYI - found a way to patch, by using the Yarn PnP API to resolve folders with package.json, in the node-core-library/PackageJsonLookup class. It seems this is the root cause in my case.

sammagee avatar Jun 08 '23 20:06 sammagee

Could you share the patch you applied?

stepanhruda avatar Jul 17 '23 19:07 stepanhruda

@stepanhruda Roughly this:

--- a/lib/PackageJsonLookup.js
+++ b/lib/PackageJsonLookup.js
@@ -123,6 +123,16 @@ class PackageJsonLookup {
      * @returns an absolute path to a folder containing a package.json file
      */
     tryGetPackageFolderFor(fileOrFolderPath) {
+        // Add compatibility for Yarn PnP
+        if (process.versions.pnp) {
+            const { findPnpApi } = require("module");
+            const pnpApi = findPnpApi(fileOrFolderPath);
+
+            return pnpApi.getPackageInformation(
+                pnpApi.findPackageLocator(fileOrFolderPath)
+            ).packageLocation;
+        }
+
         // Convert it to an absolute path
         const resolvedFileOrFolderPath = path.resolve(fileOrFolderPath);
         // Optimistically hope that the starting string is already in the cache,

Pretty dirty, but does the trick

sammagee avatar Jul 20 '23 02:07 sammagee

Thanks a lot for the workaround/patch, @sammagee! I can confirm that this also works in the project I'm currently looking at. I can't speak to how dirty this is, but it would be great to have this fixed in the package itself so we don't need to rely on yarn patch.

alexanderbartl avatar Nov 13 '23 14:11 alexanderbartl

Would be great if this could be fixed, as right it means that we essentially can't use api extractor at all.

I wonder if explicitly checking for PnP is required though -- if all it needs to do is to access the package JSON, can't it use the standard way of requiring (importing) it? With yarn and pnp enabled, if I open a quick repl via yarn node and enter e.g.

require("tslib/package.json")

yarn/pnp happily resolves the package.json

> require.resolve("tslib/package.json")
'/home/user/.yarn/berry/cache/tslib-npm-2.6.0-f346bbe805-10c0.zip/node_modules/tslib/package.json'

blutorange avatar Feb 13 '24 13:02 blutorange

@iclanton Any updates on this? I have been using api-extractor with the patch by @sammagee since November, but we have to re-apply the patch with every new release of api-extractor/node-core-library, which is creating quite some manual work for us. Would be really nice to get the patch (or some other fix) included in the official release. Thanks!

alexanderbartl avatar Jul 31 '24 09:07 alexanderbartl