berry: [Bug] Prettier is unable to load plugins in PNP environment

  • I’d be willing to implement a fix

Describe the bug

When using Prettier with plugins in a PNP environment, Prettier will not be able to find its plugins.

To Reproduce

We reproduced this on Discord with this repo:

https://github.com/oliversalzburg/prettier-plugin-pnp

Running yarn prettier src/index.ts will not remove the unused import, for which the prettier-plugin-organize-imports was installed.

Additional context

It was suggested that this is due to missing PNP functionality in the SDK wrapper. Enabling usePnpif in https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-pnpify/sources/generateSdk.ts#L119 should be a valid solution to the problem.

This should probably be enabled through a CLI flag in yarn pnpify to allow an adjusted SDK wrapper to be generated.

To actually fix the issue, I also need to supply the plugin search directory to Prettier manually. Otherwise it will search in a completely inapproriate location by default.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 25 (8 by maintainers)

Commits related to this issue

Most upvoted comments

I had this problem on VSCode, and after some debugging, figured out that when prettier is calling require (or something), it’s passing an options object with a path property pointing to VSCode’s installation directory. And I figured that dropping the options object will fix the problem. So here is what I came up with for .yarn\sdks\prettier\index.js:

#!/usr/bin/env node

const {existsSync} = require(`fs`);
const external_module_ = require(`module`);
const {createRequire, createRequireFromPath} = external_module_;
const {resolve} = require(`path`);

const relPnpApiPath = "../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
  if (!process.versions.pnp) {
    // Setup the environment to be able to require prettier/index.js
    require(absPnpApiPath).setup();
  }
}

const originalModuleResolveFilename = external_module_.Module._resolveFilename;
external_module_.Module._resolveFilename = function (request, parent, isMain, options) {
    return originalModuleResolveFilename.call(this, request, parent, isMain);
}

// Defer to the real prettier/index.js your application uses
module.exports = absRequire(`prettier/index.js`);

(Please note the relPnpApiPath variable. In my case, I had to change the extension from .js to .cjs. Change it according to your configuration.)

I’ll see if we can release a new version of @yarnpkg/pnpify, in the meantime here is the updated version

#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../.pnp.js";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
  if (!process.versions.pnp) {
    // Setup the environment to be able to require prettier/index.js
    require(absPnpApiPath).setup();
  }

  let pnpifyResolution;
  try {
    pnpifyResolution = absRequire.resolve(`@yarnpkg/pnpify`);
  } catch (err) {}
  
  if (pnpifyResolution) {
    if (typeof global[`__yarnpkg_sdk_is_using_pnpify__`] === `undefined`) {
      Object.defineProperty(global, `__yarnpkg_sdk_is_using_pnpify__`, {configurable: true, value: true});

      process.env.NODE_OPTIONS += ` -r ${pnpifyResolution}`;

      // Apply PnPify to the current process
      absRequire(pnpifyResolution).patchFs();
    }
  }
}

// Defer to the real prettier/index.js your application uses
module.exports = absRequire(`prettier/index.js`);

Waiting for release !

I’ll see if we can release a new version of @yarnpkg/pnpify, in the meantime here is the updated version

#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../.pnp.js";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
  if (!process.versions.pnp) {
    // Setup the environment to be able to require prettier/index.js
    require(absPnpApiPath).setup();
  }

  let pnpifyResolution;
  try {
    pnpifyResolution = absRequire.resolve(`@yarnpkg/pnpify`);
  } catch (err) {}
  
  if (pnpifyResolution) {
    if (typeof global[`__yarnpkg_sdk_is_using_pnpify__`] === `undefined`) {
      Object.defineProperty(global, `__yarnpkg_sdk_is_using_pnpify__`, {configurable: true, value: true});

      process.env.NODE_OPTIONS += ` -r ${pnpifyResolution}`;

      // Apply PnPify to the current process
      absRequire(pnpifyResolution).patchFs();
    }
  }
}

// Defer to the real prettier/index.js your application uses
module.exports = absRequire(`prettier/index.js`);

It still does not work for me.

["INFO" - 2:50:12 PM] Extension Name: esbenp.prettier-vscode.
["INFO" - 2:50:12 PM] Extension Version: 5.8.0.
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:50:13 PM] Enabling prettier for languages: ansible, css, graphql, home-assistant, html, javascript, javascriptreact, json, json5, jsonc, less, markdown, mdx, mongo, postcss, scss, typescript, typescriptreact, vue, yaml
["INFO" - 2:50:13 PM] Enabling prettier for file extensions: .JSON-tmLanguage, ._js, .avsc, .bones, .cjs, .component.html, .css, .es, .es6, .frag, .geojson, .gltf, .gql, .graphql, .graphqls, .gs, .har, .htm, .html, .html.hl, .ice, .inc, .jake, .js, .js.flow, .jsb, .jscad, .jsfl, .jsm, .json, .json5, .jsonc, .jss, .jsx, .less, .markdown, .mcmeta, .md, .mdown, .mdwn, .mdx, .mir, .mjml, .mjs, .mkd, .mkdn, .mkdown, .njs, .pac, .pcss, .postcss, .reek, .ronn, .rviz, .scss, .sjs, .ssjs, .st, .sublime-build, .sublime-commands, .sublime-completions, .sublime-keymap, .sublime-macro, .sublime-menu, .sublime-mousemap, .sublime-project, .sublime-settings, .sublime-syntax, .sublime-theme, .sublime-workspace, .sublime_metrics, .sublime_session, .syntax, .tfstate, .tfstate.backup, .topojson, .ts, .tsx, .vue, .webapp, .webmanifest, .workbook, .wxs, .wxss, .xht, .xhtml, .xsjs, .xsjslib, .yaml, .yaml-tmlanguage, .yaml.sed, .yml, .yml.mysql, .yy, .yyp
["INFO" - 2:50:13 PM] Enabling prettier for range supported languages: graphql, javascript, javascriptreact, json, typescript, typescriptreact
["INFO" - 2:57:11 PM] Formatting /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/packages/node-firebird-driver/src/test/tests.ts
["INFO" - 2:57:11 PM] Using ignore file (if present) at /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/packages/node-firebird-driver/.prettierignore
["INFO" - 2:57:11 PM] Loaded module 'prettier@2.2.1' from '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/sdks/prettier/index.js'
["INFO" - 2:57:11 PM] File Info:
{
  "ignored": false,
  "inferredParser": "typescript"
}
["INFO" - 2:57:11 PM] Detected local configuration (i.e. .prettierrc or .editorconfig), VS Code configuration will not be used
["INFO" - 2:57:11 PM] Using config file at '/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.prettierrc.json'
["INFO" - 2:57:11 PM] Prettier Options:
{
  "filepath": "/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/packages/node-firebird-driver/src/test/tests.ts",
  "parser": "typescript",
  "useTabs": true,
  "tabWidth": 4,
  "singleQuote": true,
  "trailingComma": "none",
  "arrowParens": "avoid",
  "plugins": ["prettier-plugin-organize-imports"]
}
["ERROR" - 2:57:11 PM] Error formatting document.
["ERROR" - 2:57:11 PM] Qualified path resolution failed - none of the candidates can be found on the disk.

Source path: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.js
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.json
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.node
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.mjs

Require stack:
- /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js
- /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js
Error: Qualified path resolution failed - none of the candidates can be found on the disk.

Source path: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.js
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.json
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.node
Rejected candidate: /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/node_modules/prettier-plugin-organize-imports/index.js.mjs

Require stack:
- /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js
- /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js
	at internalTools_makeError (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js:12770:34)
	at resolveUnqualified (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js:13803:13)
	at resolveRequest (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js:13827:14)
	at Object.resolveRequest (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js:13899:26)
	at Function.external_module_.Module._resolveFilename (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js:13003:34)
	at Function.external_module_.Module._load (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.pnp.js:12868:48)
	at Module.require (internal/modules/cjs/loader.js:899:19)
	at r (/snap/code/52/usr/share/code/resources/app/out/vs/loader.js:16:346)
	at /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js:57503:6
	at Array.map (<anonymous>)
	at Object.load (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js:57501:118)
	at Object.load [as loadPlugins] (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js:16612:23)
	at /home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js:57540:28
	at Object.format (/home/asfernandes/projects/github/node-fb/node-firebird-drivers.git/.yarn/cache/prettier-npm-2.2.1-e0670992f8-92c6c9f4b8.zip/node_modules/prettier/index.js:57562:12)
	at t.default.<anonymous> (/home/asfernandes/.vscode/extensions/esbenp.prettier-vscode-5.8.0/dist/extension.js:1:17599)
	at Generator.next (<anonymous>)
	at s (/home/asfernandes/.vscode/extensions/esbenp.prettier-vscode-5.8.0/dist/extension.js:1:11597)
["INFO" - 2:57:11 PM] Formatting completed in 135.951654ms.

It hasn’t been released yet, but you can apply it manually by editing .yarn\sdks\prettier\index.js and changing it to

#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve, dirname} = require(`path`);

const relPnpApiPath = "../../../.pnp.js";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
  if (!process.versions.pnp) {
    // Setup the environment to be able to require prettier/index.js
    require(absPnpApiPath).setup();
  }

  const pnpifyResolution = require.resolve(`@yarnpkg/pnpify`, {paths: [dirname(absPnpApiPath)]});
  if (typeof global[`__yarnpkg_sdk_is_using_pnpify__`] === `undefined`) {
    Object.defineProperty(global, `__yarnpkg_sdk_is_using_pnpify__`, {configurable: true, value: true});

    process.env.NODE_OPTIONS += ` -r ${pnpifyResolution}`;

    // Apply PnPify to the current process
    absRequire(pnpifyResolution).patchFs();
  }
}

// Defer to the real prettier/index.js your application uses
module.exports = absRequire(`prettier/index.js`);

I’ve tried replacing .yarn\sdks\prettier\index.js as prescribed, that didn’t do anything. Then I tried listing/requiring the plugins explicitly in the config, like here, to no avail, I get a Qualified path resolution failed error. The plugin I’m trying this with is prettier-plugin-tailwind. Am I missing something?

You’re missing @yarnpkg/pnpify as a dependency

I’ve it installed as described in the docs (as dependency - not devDependency)!

My project uses workspaces and to be sure, I’ve installed it in the root and all sub packages.

@asfernandes After discussion we decided to enable pnpify runtime on a per-tool basis and not shift the decision to the users, because they don’t know the tradeoffs involved. For prettier we will always enable pnpify runtime starting from the next @yarnpkg/pnpify version: https://github.com/yarnpkg/berry/pull/1913