format-imports-vscode icon indicating copy to clipboard operation
format-imports-vscode copied to clipboard

[Regression] Extension no-longer support Yarn and PnP packages

Open Swiftwork opened this issue 3 years ago • 8 comments

Describe the bug Versions v7.4.13 to v7.4.16 can no longer find Prettier and Eslint modules. While the plugin does work with older version v7.4.12. Assumed to be related to PnP according to https://github.com/daidodo/format-imports-vscode/issues/70#issuecomment-1094722955

To Reproduce Steps to reproduce the behavior: Reproduction

Expected behavior Continue working as it has :).

Actual behavior

[2022-04-08T14:03:30.406] [INFO] default - os: {
  arch: 'x64',
  platform: 'win32',
  type: 'Windows_NT',
  release: '10.0.22000',
  totalmem: 17024741376,
  freemem: 6524624896,
  EOL: '\r\n'
}
[2022-04-08T14:03:30.407] [INFO] default - vscode: { version: '1.66.1' }
[2022-04-08T14:03:34.342] [INFO] vscode.resolveConfig - Resolving config for fileName: C:\path\to\file.tsx languageId: typescriptreact
[2022-04-08T14:03:34.344] [WARN] format-imports.requireModule - Cannot find prettier from path: C:\path\to\file.tsx thus use pre-packed
[2022-04-08T14:03:34.344] [WARN] format-imports.loadPretConfig - Failed to load Prettier/EditorConfig config for filename: C:\path\to\file.tsx with error: Cannot read properties of undefined (reading 'sync')
[2022-04-08T14:03:34.384] [WARN] format-imports.requireModule - Cannot find eslint from path: C:\path\to\file.tsx thus use pre-packed
[2022-04-08T14:03:34.401] [WARN] format-imports.loadESLintConfig - Failed to load ESLint config for fileName: C:\path\to\file.tsx with error: Cannot read config file: C:\path\to\project\.yarn\__virtual__\eslint-config-next-virtual-8bde479ba4\0\cache\eslint-config-next-npm-12.0.10-8e22703dc2-37f84056f8.zip\node_modules\eslint-config-next\core-web-vitals.js
Error: Cannot find module 'C:\path\to\project\.yarn\__virtual__\eslint-config-next-virtual-8bde479ba4\0\cache\eslint-config-next-npm-12.0.10-8e22703dc2-37f84056f8.zip\node_modules\eslint-config-next\core-web-vitals.js'
Referenced from: C:\path\to\project\.eslintrc.json
[2022-04-08T14:03:34.401] [INFO] format-imports.formatSource - Formatting C:\path\to\file.tsx with enhanced config: {
  configurationFileName: 'import-sorter.json',
  autoFormat: 'onSave',
  formatExports: true,
  exclude: [ 'node_modules', 'gen' ],
  excludeGlob: [],
  sortImportsBy: 'paths',
  groupRules: [
    '^react(-dom)?$',
    '^@angular/',
    '^vue$',
    '^node:',
    {},
    '^[@]',
    '^[.]'
  ],
  sortRules: { paths: [ '_', 'aA' ], names: [ '_', 'aA' ] },
  keepUnused: [],
  emptyLinesBetweenGroups: 1,
  emptyLinesAfterAllImports: 1,
  removeLastSlashInPath: false,
  removeLastIndexInPath: false,
  wrappingStyle: 'prettier',
  development: { enableDebug: false },
  insertFinalNewline: 'preserve',
  eof: '\n',
  eol: 'LF',
  force: false
}
[2022-04-08T14:03:34.485] [INFO] vscode.formatDocument - Finished format

OS and VS Code:

Version: 1.66.1 (user setup)
Date: 2022-04-06T14:50:12.141Z
Electron: 17.2.0
Chromium: 98.0.4758.109
Node.js: 16.13.0
V8: 9.8.177.11-electron.0
OS: Windows_NT x64 10.0.22000

package.json

{
  "name": "reproduction-tsimport",
  "packageManager": "[email protected]",
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.18.0",
    "@typescript-eslint/parser": "^5.18.0",
    "eslint": "^8.12.0",
    "prettier": "^2.6.2"
  }
}

.prettierrc/.prettierrc.js/prettier.config.js/.prettierrc.toml filename: .prettierrc.js

module.exports = {
  bracketSameLine: true,
  singleQuote: true,
  printWidth: 120,
  trailingComma: 'all',
};

.editorconfig

root = true

[*]
end_of_line = lf
insert_final_newline = true

[*.{js,json,yml}]
charset = utf-8
indent_style = space
indent_size = 2

Additional information

Yarn works very similarly to npm and instructions can be found here https://yarnpkg.com/getting-started/migration. The reproduction should probably not need any installation to work as it uses the Zero-installs mode, but you can run yarn install in the command line to make sure nothing is missing. This requires yarn v1 to be installed globally though.

Swiftwork avatar Apr 11 '22 13:04 Swiftwork

Thanks for the feedback!

May I ask how to install yarn v1? My version is:

$ yarn -v
3.2.0

After yarn install I don't find node_modules but .yarn/.... PnP has changed the behaviour of dependency lookup. I think that's the reason why the plugin can't find Prettier and ESlint. (Another factor is that from v7.4.13 the plugin is webpacked, see https://github.com/daidodo/format-imports-vscode/issues/53).

Currently, I don't see an easy fix for the issue. But I'd be happy to accept PRs if you or anyone wants to help.

daidodo avatar Apr 13 '22 08:04 daidodo

To answer some of your questions:

  1. The yarn version is probably correct. Yarn is included through corepack in node version 16, but if you are running an earlier version follow this page https://yarnpkg.com/getting-started/install. When I check my yarn version it is different depending on if I am in the project directory or not (this is intentional).
C:\Projects\Git\@swiftwork\reproduction-tsimport>yarn --version
3.2.0

C:\Projects\Git\@swiftwork\reproduction-tsimport>cd ..

C:\Projects\Git\@swiftwork>yarn --version
1.22.15
  1. Yarn stores dependencies in .yarn/ (archives in .yarn/cache) folder as you might have noticed and will not install to node_modules unless you add nodeLinker: node-modules in the .yarnrc.yml file. I am fairly new to the yarn ecosystem as well, but one of the pnp files should be the module resolver. In typescript the import works the same with pnp and node_modules as long as you run the script using yarn as this bootstraps the pnp loader.

  2. Maybe the webpack section can give you some hints. https://yarnpkg.com/getting-started/migration#enable-the-pnp-plugin-when-using-webpack-4

Swiftwork avatar Apr 13 '22 14:04 Swiftwork

Thanks for the sharing!

The plugin needs to look up ESLint (node_modules/eslint) and Prettier (node_modules/prettier) in the user workspace to understand their configs. Apparently the new PnP module structure is not compatible.

I wonder if there are any public APIs from PnP/yarn that do the work.

daidodo avatar Apr 14 '22 08:04 daidodo

I'll do some more digging into supporting both node_modules and PNP, but these seem to me to be the relevant changes where the extension went from working with PNP to not. v7.4.12 -> v7.4.13

https://github.com/daidodo/format-imports-vscode/compare/03b00867836e131f95ec7b4aa86ba26aa7d811ba...7ccd4715932fb6b203567c501eebb3c923106848

https://github.com/daidodo/format-imports/compare/2923b8c50553c2578e805be751ee3829f40a091c...b8801fe8fcec35948a06945e1f95a31d9134c39e

Swiftwork avatar Apr 14 '22 14:04 Swiftwork

I think I figured out how it is solved for the vscode-eslint and vscode-prettier extensions. Not sure what the best solution for this extension will be but the findings might be worth checking out. Essentially due to the resolving issue, there is a yarn command yarn dlx @yarnpkg/sdks vscode that "bootstraps" the tools in vscode. This results in the paths explicitly set in the setting.json.

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "search.exclude": {
    "**/.pnp.*": true,
    "**/.yarn": true
  },
  "tsImportSorter.configuration.wrappingStyle": "prettier",
  "eslint.nodePath": ".yarn/sdks",
  "prettier.prettierPath": ".yarn/sdks/prettier/index.js"
}

One possibility would be to use these paths in this packages resolve step. My guess it was working before due to eslint and prettier being included and shipped somewhere in this extension, but that changed with the new webpack build.

https://github.com/prettier/prettier-vscode/issues/1502

Swiftwork avatar Apr 14 '22 15:04 Swiftwork

@daidodo Any progress on this? Do you think you could implement two new properties?

{
    "tsImportSorter.configuration.prettierPath": ".yarn/sdks/prettier/index.js",
    "tsImportSorter.configuration.eslintPath": ".yarn/sdks/eslint/bin/eslint.js"
}

Swiftwork avatar May 17 '22 20:05 Swiftwork

Sorry, I'm super busy recently.

Could you give more details on how that would fix the issue?

I don't think I'll have time to implement a fix soon so if you or anyone wants to help, I'd be very grateful!

daidodo avatar May 18 '22 00:05 daidodo

@daidodo Thank you for the quick response. I fully understand hard to find the time to maintain open-source libraries. So my thinking with the above settings would be to follow the pattern detailed on the Yarn PnP page about editor sdks. When running the command mentioned for VSCode, Yarn will create a wrapper around the tool (eslint, prettier...) in .yarn/sdks/prettier/index.js that looks like this:

#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`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();
  }
}

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

My idea would be that you add to new configuration options to allow developers to specify the prettier/eslint paths and use these first to resolve the module locations then fall back to the requireModule code that is currently used. Alternatively, you could use the existing plugins' two configurations to resolve the paths:

{
  "eslint.nodePath": ".yarn/sdks",
  "prettier.prettierPath": ".yarn/sdks/prettier/index.js"
}

Regarding assistance, I might be able to help but it would take me a little time to understand the projects. It's also a little more complicated as resolving the modules is done in the core library while the configurations are set in this one.

Swiftwork avatar May 18 '22 14:05 Swiftwork