daidodo / format-imports-vscode

Format imports and exports for JavaScript and TypeScript in VS Code.
https://marketplace.visualstudio.com/items?itemName=dozerg.tsimportsorter
MIT License
62 stars 5 forks source link

Formatting imports jumps between prettier formatting #91

Open Nxt3 opened 1 year ago

Nxt3 commented 1 year ago

Describe the bug Auto Imports seems to be interfering with prettier formatting still. Might be related to https://github.com/daidodo/format-imports-vscode/issues/39

To Reproduce Format this file:

import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';

// use the imports to avoid removal
console.log('HttpResponse', HttpResponse);
console.log('HttpParams', HttpParams);
console.log('HttpHeaders', HttpHeaders);
console.log('HttpErrorResponse', HttpErrorResponse);
console.log('HttpClient', HttpClient);

with following VS Code settings:

  "tsImportSorter.configuration.autoFormat": "off",
  "tsImportSorter.configuration.development.enableDebug": false,
  "tsImportSorter.configuration.wrappingStyle": "prettier",

  "[typescript]": {
    "editor.codeActionsOnSave": [
      "source.addMissingImports",
      "source.organizeImports.sortImports",
      "source.formatDocument",
      "source.fixAll.eslint"
    ]
  },

Expected behavior TS Import sorter should allow prettier to handle the formatting for line length.

Actual behavior Formatting for long lines jumps between formatted an unformatted.

Screenshots import_formatting_issues

OS (please complete the following information):

VS Code (please complete the following information):

Version: 1.72.0-insider (Universal)
Commit: 64bbfbf67ada9953918d72e1df2f4d8e537d340e
Date: 2022-10-04T23:18:32.134Z
Electron: 19.0.17
Chromium: 102.0.5005.167
Node.js: 16.14.2
V8: 10.2.154.15-electron.0
OS: Darwin arm64 21.6.0
Sandboxed: Yes

Installed VS Code extensions

rohit-gohri.format-code-action: https://github.com/rohit-gohri/vscode-format-code-action

.prettier.yml

tabWidth: 2
semi: true
singleQuote: true
printWidth: 100
trailingComma: none

.eslintrc.json

{
  "root": true,
  "ignorePatterns": [
    "projects/**/*",
    "!**/*.ts",
    "!**/*.html"
  ],
  "overrides": [
    {
      "files": [
        "*.ts"
      ],
      "parser": "@typescript-eslint/parser",
      "parserOptions": {
        "ecmaVersion": 2018,
        "project": [
          "tsconfig.json"
        ],
        "createDefaultProgram": false
      },
      "plugins": [
        "ban"
      ],
      "extends": [
        "plugin:@angular-eslint/recommended",
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@typescript-eslint/recommended-requiring-type-checking",
        "plugin:@angular-eslint/template/process-inline-templates",
        "plugin:@angular-eslint/recommended--extra"
      ],
      "rules": {
        "@angular-eslint/component-class-suffix": "error",
        "@angular-eslint/contextual-lifecycle": "warn",
        "@angular-eslint/directive-class-suffix": "error",
        "@angular-eslint/no-conflicting-lifecycle": "warn",
        "@angular-eslint/no-forward-ref": "off",
        "@angular-eslint/no-host-metadata-property": "warn",
        "@angular-eslint/no-input-rename": "warn",
        "@angular-eslint/no-output-native": "warn",
        "@angular-eslint/no-output-on-prefix": "warn",
        "@angular-eslint/no-output-rename": "error",
        "@angular-eslint/use-lifecycle-interface": "error",
        "@typescript-eslint/adjacent-overload-signatures": "warn",
        "@typescript-eslint/await-thenable": "warn",
        "@typescript-eslint/ban-ts-comment": "warn",
        "@typescript-eslint/ban-types": "warn",
        "@typescript-eslint/explicit-module-boundary-types": "off",
        "@typescript-eslint/indent": [
          "error",
          2
        ],
        "@typescript-eslint/member-ordering": "warn",
        "@typescript-eslint/naming-convention": [
          "error",
          {
            "selector": "class",
            "format": [
              "PascalCase"
            ]
          }
        ],
        "@typescript-eslint/no-empty-function": "error",
        "@typescript-eslint/no-empty-interface": "warn",
        "@typescript-eslint/no-explicit-any": "warn",
        "@typescript-eslint/no-floating-promises": "warn",
        "@typescript-eslint/no-inferrable-types": "off",
        "@typescript-eslint/no-misused-promises": "warn",
        "@typescript-eslint/no-namespace": "warn",
        "@typescript-eslint/no-shadow": "error",
        "@typescript-eslint/no-this-alias": "warn",
        "@typescript-eslint/no-unnecessary-type-assertion": "warn",
        "@typescript-eslint/no-unsafe-assignment": "warn",
        "@typescript-eslint/no-unsafe-call": "warn",
        "@typescript-eslint/no-unsafe-member-access": "warn",
        "@typescript-eslint/no-unsafe-return": "warn",
        "@typescript-eslint/no-use-before-define": [
          "warn",
          {
            "functions": false,
            "classes": false,
            "typedefs": true
          }
        ],
        "@typescript-eslint/no-var-requires": "warn",
        "@typescript-eslint/prefer-regexp-exec": "warn",
        "@typescript-eslint/require-await": "off",
        "@typescript-eslint/restrict-plus-operands": "warn",
        "@typescript-eslint/restrict-template-expressions": "warn",
        "@typescript-eslint/type-annotation-spacing": "error",
        "@typescript-eslint/unbound-method": "warn",
        "ban/ban": [
          "error",
          {
            "name": "fit",
            "message": "Don't keep jasmine focus methods"
          },
          {
            "name": "fdescribe",
            "message": "Don't keep jasmine focus methods"
          }
        ],
        "brace-style": [
          "error",
          "1tbs"
        ],
        "curly": "error",
        "eol-last": "error",
        "eqeqeq": [
          "error",
          "always",
          {
            "null": "ignore"
          }
        ],
        "getter-return": "warn",
        "id-blacklist": "off",
        "id-match": "off",
        "indent": "off",
        "keyword-spacing": "error",
        "max-len": [
          "error",
          {
            "ignorePattern": "^import [^,]+ from |^export | implements | ('|\")(http|https):",
            "code": 140
          }
        ],
        "no-bitwise": "error",
        "no-caller": "error",
        "no-console": "error",
        "no-debugger": "error",
        "no-duplicate-imports": "error",
        "no-empty": "error",
        "no-empty-pattern": "warn",
        "no-eval": "error",
        "no-extra-boolean-cast": "warn",
        "no-fallthrough": "warn",
        "no-irregular-whitespace": "error",
        "no-new-wrappers": "error",
        "no-prototype-builtins": "warn",
        "no-redeclare": "warn",
        "no-restricted-imports": [
          "error",
          {
            "paths": [
              {
                "name": "@synerg/components",
                "message": "Please specify full Synerg component path starting with @synerg/components/"
              },
              {
                "name": "rxjs/Rx",
                "message": "No import from rxjs/Rx"
              },
              {
                "name": "rxjs/src",
                "message": "No import from rxjs/src. Import from rxjs"
              },
              {
                "name": "rxjs/internal/operators",
                "message": "No import from rxjs/internal/operators. Import from rxjs/operators"
              },
              {
                "name": "rxjs/internal",
                "message": "No import from rxjs/internal. Import from rxjs"
              },
              {
                "name": "rxjs/index",
                "message": "No import from rxjs/index. Import from rxjs"
              },
              {
                "name": "node_modules",
                "message": "No import from node_modules"
              }
            ],
            "patterns": ["rxjs/internal/*"]
          }
        ],
        "no-trailing-spaces": "error",
        "no-underscore-dangle": "off",
        "no-unused-expressions": "warn",
        "no-unused-vars": [
          "error",
          {
            "argsIgnorePattern": "^_"
          }
        ],
        "no-useless-escape": "warn",
        "no-var": "warn",
        "object-curly-spacing": [
          "error",
          "always"
        ],
        "prefer-const": "warn",
        "prefer-rest-params": "warn",
        "semi-spacing": "error",
        "quotes": [
          "error",
          "single",
          {
            "allowTemplateLiterals": true,
            "avoidEscape": true
          }
        ],
        "semi": "error",
        "semi-style": [
          "error",
          "last"
        ],
        "space-before-function-paren": [
          "error",
          {
            "anonymous": "never",
            "named": "never",
            "asyncArrow": "always"
          }
        ],
        "space-in-parens": "error",
        "spaced-comment": "error"
      }
    },
    {
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended"
      ],
      "parser": "@angular-eslint/template-parser",
      "rules": {
        "@angular-eslint/template/no-negated-async": "warn",
        "@angular-eslint/template/eqeqeq": "warn"
      }
    }
  ]
}
daidodo commented 1 year ago

Thanks for the feedback!

You can set line wrapping style to be compatible with Prettier. More details: https://github.com/daidodo/format-imports/wiki/Line-Wrapping-Style#prettier-compatibility

Please tell me if you still have questions.

Nxt3 commented 1 year ago

I already have this setting enabled: "tsImportSorter.configuration.wrappingStyle": "prettier" and it doesn't seem to fix the problem. Can there be a setting to only sort/organize? Having this extension also handle formatting seems more problematic than it's worth given it can easily conflict with external formatting tools.

Nxt3 commented 1 year ago

The correct output should be having those imports split across several lines. If I disable this extension and let prettier handle formatting, the code is formatted properly.

If I disable prettier and let this extension format the code, the output is incorrect with all of the imports on a single line, even with "tsImportSorter.configuration.wrappingStyle": "prettier"

Disabling eslint has no effect on the formatting.

jamesgpearce commented 1 year ago

I had the "jumping around on every save" issue, and setting "tsImportSorter.configuration.wrappingStyle": "prettier" fixed it for me. Thank you.

Nxt3 commented 1 year ago

I had the "jumping around on every save" issue, and setting "tsImportSorter.configuration.wrappingStyle": "prettier" fixed it for me. Thank you.

In the above, I already have this setting and it hasn't fixed the issue.

Nxt3 commented 1 year ago

The real fix here would be to remove formatting responsibilities from this extension and have it purely handle sorting.

binarykitchen commented 7 months ago

I'm having this issue too and it's very frustrating.

remove formatting responsibilities

@Nxt3 How?

Nxt3 commented 7 months ago

I don't have an answer to that. Someone needs to submit a PR with those changes. There isn't a fix for this currently.

daidodo commented 7 months ago

Sorry for the late response!

The long-term solution is to migrate the extension to a Prettier plugin. But I don't have time ATM. I'd appreciate if anyone want to help.

Another workaround is to add the following settings with help of this extension:

  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": [
    "source.formatDocument",
    "source.organizeImports.sortImports"
  ],
Nxt3 commented 7 months ago

Unfortunately that doesn't solve the problem as it just makes TS Importer/Sorter the formatter for imports; if someone else working in your project doesn't have this VS Code extension, their Prettier will undo the formatting changes causing an unnecessary diff.

daidodo commented 7 months ago

Did you try to swap the code actions so prettier can prevail?

Nxt3 commented 7 months ago
    "editor.codeActionsOnSave": [
      "source.addMissingImports",
      "source.organizeImports.sortImports",
      "source.formatDocument", // prettier
      "source.fixAll.eslint"
    ]

It still causes the imports to change formatting on every save.

This is referenced in the OP

Nxt3 commented 7 months ago

I think I fixed this by adding another prettier call:

    "editor.codeActionsOnSave": [
      "source.addMissingImports",
      "source.organizeImports.sortImports",
      "source.formatDocument",
      "source.formatDocument",
      "source.fixAll.eslint"
    ],

I not longer have the jump in formatting.

binarykitchen commented 4 months ago

It's frustrating. Thinking of uninstalling this extension, I'm afraid.

Nxt3 commented 4 months ago

I think I came up with a better solution when compared to running formatDocument twice.

Download this extension: stereokai.vscode-run-commands-as-code-actions

It allows us to add delays to code actions run on save so you can "waterfall" the commands.

Add this to your settings.json:

  "run-commands-as-code-actions.jsTsFormatAndLint": [
    {
      "command": "tsImportSorter.command.sortImports",
      "delay": 100
    },
    {
      "command": "prettier.forceFormatDocument",
      "delay": 150
    },
    {
      "command": "eslint.executeAutofix",
      "delay": 200
    },
    {
      "command": "workbench.action.files.saveWithoutFormatting",
      "delay": 250
    }
  ],
    "[typescript]": {
    // here's the magic
    "editor.codeActionsOnSave": ["source.addMissingImports", "source.runCommandsAsCodeActions.jsTsFormatAndLint"],
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": false,
  },

This will add missing imports, then run the actions defined under jsTsFormatAndLint: Sort imports, format using prettier, eslint fix, and then save the file. This has been working great for me for a couple of weeks now.

binarykitchen commented 4 months ago

Thanks for sharing, still a problem with the delays of 100, 150 or 200 - how will you know the previous action did really complete before starting the next one? Especially in large code bases.

Nxt3 commented 4 months ago

Thanks for sharing, still a problem with the delays of 100, 150 or 200 - how will you know the previous action did really complete before starting the next one? Especially in large code bases.

The extension author could probably answer that better.

jamesgpearce commented 3 weeks ago

After months of working fine, I got this again (after moving some eslint config files around). Very frustrating. But then I restarted VSCode and it worked again. Shrug.

jamesgpearce commented 2 weeks ago

....and it happened again. I had to restart a couple of times before it worked correctly. My guess is that every time you start VS Code, the 'order' of extensions that can format-on-save changes, and you just have to keep restarting until Prettier beats this (or vv, which ever you prefer)