prettier icon indicating copy to clipboard operation
prettier copied to clipboard

[Plugin API] Multiple prettier plugins accessing the same language disables all but one

Open SanderRonde opened this issue 3 years ago • 9 comments

Environments:

  • Prettier Version: 2.6.2
  • Running Prettier via: Node.js API
  • Runtime: 18.1.0
  • Operating System: Linux (through WSL)
  • Prettier plugins (if any): prettier-plugin-tailwindcss, prettier-plugin-sort-imports (disclosure: I made this plugin, issue also happens with @trivago/prettier-plugin-sort-imports though)

Steps to reproduce:

  • Install two plugins that both register a preprocessor/parser for the same language
  • One of them stops working

Expected behavior:

They both work

Actual behavior:

One of them stops working (probably dependent on alphabetical order)

The issue

I did some digging and the issue is actually pretty fundamental. It seems to be common practice to register prettier plugins that handle existing languages in this way:

module.exports = {
  parsers: {
    typescript: {
      ...require('prettier/parser-typescript'),
      preprocess: (...args) => {
        require('prettier/parser-typescript').parsers.typescript.preprocess(...args);
        myCustomPreProcessFunction(...args);
      }
    }
  }
}

However, when two prettier plugins add their own preprocessors (or handlers or whatever) for the same language, things break. Because parsers.typescript is re-assigned to a fresh object, and since require('prettier/parser-typescipt') is not modified in-place, the new object won't have the previous plugins' handlers attached. This means that any double (or more) registration of a language just disables all plugins except for one. In the case of prettier-plugin-tailwindcss this is particularly bad because it registers 18 parsers.

This could be fixed by requiring plugin authors to modify require('prettier/parser-typescript') in-place, for example:

const parserTypescript = require('prettier/parser-typescript');
parserTypescript.parsers.typescript.preprocess = (...args) => {
    parserTypescript.parsers.typescript.preprocess(...args);
    myCustomPreProcessFunction(...args);
}

module.exports = { parsers: {typescript: parserTypescript.parsers.typescript } };

But I'd say a plugin system that requires plugins to export a callback might be better here. That way they'll always have access to the "current" parser and don't need to import it themselves.

What are your thoughts on this? Has this been thought about before (I can't imagine nobody has ran into this issue before)? Or am I doing something wrong?

SanderRonde avatar May 07 '22 22:05 SanderRonde

I have the same issue

  plugins: [
    require("prettier-plugin-tailwindcss"),
    require("@trivago/prettier-plugin-sort-imports"),
  ],

only the second one works at a time

mzalevski avatar May 23 '22 16:05 mzalevski

I have the same behavior using pnpm with this config:

// .prettierrc.cjs

/** @type {import('prettier').Config} */
module.exports = {
  plugins: [
    require('prettier-plugin-jsdoc'),
    require('@trivago/prettier-plugin-sort-imports')
  ],
  printWidth: 100,
  proseWrap: 'always',
  singleQuote: true,
  semi: false,
  importOrder: ['^node:', '<THIRD_PARTY_MODULES>', '^@mheob/(.*)$', '^[./]'],
  importOrderSeparation: true,
  importOrderSortSpecifiers: true,
  overrides: [
    {
      files: '*.{yaml,yml}',
      options: {
        printWidth: 130,
        singleQuote: false,
      },
    },
  ],
}

Environments:

Prettier Version: 2.7.1 Running Prettier via: Node.js 18.7.0 / PNPM 7.8.0 Operating System: MacOS Prettier plugins (if any): prettier-plugin-jsdoc, @trivago/prettier-plugin-sort-imports

mheob avatar Aug 02 '22 05:08 mheob

Same for me with plugins: ['@trivago/prettier-plugin-sort-imports', 'prettier-plugin-tailwindcss'] and only the second one works.

lucasrabiec avatar Sep 18 '22 18:09 lucasrabiec

Just ran into this myself. There's a great workaround over in a related issue in the prettier-plugin-tailwindcss repo. Convert your .prettierrc to prettier.config.js, manually mash the two plugins together, and pass that to Prettier in the config:

https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/31#issuecomment-1195411734

markerikson avatar Oct 20 '22 18:10 markerikson

@markerikson like I found out in comment https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/31#issuecomment-1304815626, that solution breaks for pnpm for some reason. Probably module resolution?

filipesmedeiros avatar Nov 06 '22 14:11 filipesmedeiros

Same for me for pnpm. Only the second one works

  plugins: [
    require('prettier-plugin-tailwindcss'),
    require('@trivago/prettier-plugin-sort-imports'),
  ],

Ali-Parandeh avatar Nov 17 '22 16:11 Ali-Parandeh

I am also experiencing this issue.

Setup: Vite, React, TypeScript+SWC

I have two plugins autoloading, @trivago/prettier-plugin-sort-imports was installed first, then prettier-plugin-tailwindcss.

As soon as I remove trivago, hit save in my file, prettier sorts my classes with the tailwindcss plugin. Reinstall trivago and my imports get sorted, but not my classes.

Autoloading appears to favour only one package based on name sorting.

EDIT: tried the workaround markerikson mentioned and was successful, had to call my config prettier.config.cjs for my particular setup.

jameswilliamknight avatar Jan 31 '23 22:01 jameswilliamknight

I am seeing the same issue when using @trivago/prettier-plugin-sort-imports and prettier-plugin-tailwindcss with pnpm, however, it seems the first plugin in the plugins array is used instead of the second one as others have observed.

I am using Eslint 7.32.0.

johanbook avatar May 31 '23 06:05 johanbook