[api-extractor] Yarn PnP - "ENOENT: package.json not found"
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
- Create a monorepo with yarn workspaces and PnP set up. (Perhaps using something like this would be helpful)
- 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 theinstalltime and disk space) - using
yarn patch @microsoft/api-extractor && yarn patch @microsoft/api-extractor-modelthen 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 |
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.
Could you share the patch you applied?
@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
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.
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'
@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!