lydell / eslint-plugin-simple-import-sort

Easy autofixable import sorting.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support multiple `<script>` in `.vue` files

achaphiv opened this issue · comments

Version:

=> Found "eslint-plugin-simple-import-sort@7.0.0"

My .eslintrc.js

module.exports = {
  root: true,
  env: {
    'browser': true,
    'node': true,
    // `defineProps` and `defineEmits`
    'vue/setup-compiler-macros': true,
  },
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
  },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-recommended',
    '@vue/eslint-config-prettier',
    'plugin:nuxt/recommended',
  ],
  plugins: ['simple-import-sort'],
  settings: {
    'import/resolver': {
      typescript: {},
    },
  },
  // add your custom rules here
  rules: {
    'simple-import-sort/imports': 'warn',
    'simple-import-sort/exports': 'warn',
  },
}

Vue v3 introduced <script setup>.

Sometimes you need two <script> sections. E.G.

<script setup lang="ts">
import SomeComponent from '@/components/SomeComponent.vue'
</script>

<script lang="ts">
import { defineComponent } from '@nuxtjs/composition-api'

export default defineComponent({
  layout: 'main',
})
</script>

However running this plugin produced this mangled output:

<script setup lang="ts">
import { defineComponent } from '@nuxtjs/composition-api'

import SomeComponent from '@/components/SomeComponent.vue'

export default defineComponent({
  layout: 'siteMain',
})
</script>

I.E. it incorrectly merged the two <script> into one.

I expected the two <script> tags to remain separate. So the example input given here would be unchanged.

Hi! Are you sure this is a simple-import-sort issue, and not a eslint-plugin-vue issue? Is only simple-import-sort+vue the problem, or can you get similar effects with other ESLint rules that touch multiple lines, such as curly and quotes? (See https://github.com/lydell/eslint-plugin-svelte-multiline-fix-issue for some example code.)

TLDR: I suspect this is actually an issue with vue-eslint-parser. So I will close.

I thought it was this plugin, because I had initially tried eslint-plugin-sort-imports-es6-autofix, and that plugin didn't have a problem with multiple <script> tags.

However that plugin doesn't have grouping logic, so it never actually tried to combine lines across the <script> tags.

But another plugin, eslint-plugin-import, does do <script> combining.

So it seems like vue-eslint-parser is the common factor.

@achaphiv I'm having this same issue and it only occurs when this plugin is enabled. I'm also using vue-eslint-parser, but it's really difficult to test this without using it since a bunch of other eslint issues happen when using a <script setup lang="ts"> tag as well as a <script lang="ts"> tag in a vue file when not using vue-eslint-parser.

Are you saying your script tags were combined when using eslint-plugin-import but itself, or with simple-import-sort? I tried using just eslint-plugin-import and it sorts, but it doesn't combine the script tags. It just doesn't sort as nicely as simple-import-sort in my opinion.

I ended up getting a configuration that works. The key is that the <script setup> tag needs to be after the other <script> tag.

.eslintrc.js

module.exports = {
    env: {
        browser: true,
        es2021: true,
    },
    extends: [
        'eslint:recommended',
        'plugin:vue/vue3-recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
    ],
    globals: {
        window: true,
        module: true,
    },
    overrides: [],
    parser: 'vue-eslint-parser',
    parserOptions: {
        parser: '@typescript-eslint/parser',
        ecmaVersion: 'latest',
        sourceType: 'module',
    },
    plugins: ['vue', '@typescript-eslint', 'simple-import-sort', 'import'],
    rules: {
        quotes: ['error', 'single', { avoidEscape: true }],
        '@typescript-eslint/no-explicit-any': 'off',
        '@typescript-eslint/ban-ts-comment': 'off',
        'vue/multi-word-component-names': 'off',
        'vue/component-tags-order': [
            'error',
            {
                order: ['script:not([setup])', 'script[setup]', 'template', 'style'],
            },
        ],
        'simple-import-sort/imports': [
            'error',
            {
                groups: [
                    // Packages `vue` related packages come first.
                    ['^vue', '^\\w', '^@?\\w'],
                    // View and component first
                    ['^(@/views)(/.*|$)'],
                    // Internal packages.
                    ['^(@/scripts)(/.*|$)'],
                    // Side effect imports.
                    ['^\\u0000'],
                    // Parent imports. Put `..` last.
                    ['^\\.\\.(?!/?$)', '^\\.\\./?$'],
                    // Other relative imports. Put same-folder imports and `.` last.
                    ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
                    // Style imports.
                    ['^.+\\.?(css)$'],
                ],
            },
        ],
        'simple-import-sort/exports': 'error',
        'import/newline-after-import': 'error',
        'import/no-duplicates': 'error',
    },
};

ExampleComponent (using inertia.js persistent layouts):

<script lang="ts">
import Blank from '@/views/layouts/blank.vue';

export default {
    layout: Blank,
};
</script>

<script setup lang="ts">
import route from 'ziggy-js';
import { Link } from '@inertiajs/vue3';

import FlashMessages from '@/views/components/flash-messages.vue';
import ClientLogoIconOnly from '@/views/components/logos/client-logo-icon-only.vue';
</script>

<template>
// your template
</template>

@jamiethorpe

In the end I switched to: https://github.com/trivago/prettier-plugin-sort-imports

I forget the exact details why. It just worked better for me.

As a side note, I found performance was much better to have a separate overrides just for *.vue files. It made linting overall faster when processing *.ts files.

Here's my full config for reference:

.prettierrc.cjs:

module.exports = {
  semi: false,
  singleQuote: true,
  quoteProps: 'consistent',
  arrowParens: 'always',
  htmlWhitespaceSensitivity: 'ignore',

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

  // https://github.com/trivago/prettier-plugin-sort-imports
  importOrder: [
    // `<svg>` components
    '\\.svg(\\?.+)?$',
    // Vue components (absolute)
    '^@/.*\\.vue$',
    // Vue components (relative)
    '\\.vue$',
    // External
    '<THIRD_PARTY_MODULES>',
    // Internal
    '^@/',
    // Sibling
    '^[./]',
  ],
  importOrderSeparation: true,
  importOrderSortSpecifiers: true,
}

.eslintrc.cjs:

module.exports = {
  root: true,

  env: {
    browser: true,
    node: true,
    es6: true,
  },
  parser: '@typescript-eslint/parser',
  // https://eslint.org/docs/user-guide/configuring/plugins#naming-convention
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
    'plugin:import/typescript',
  ],
  plugins: ['@typescript-eslint', 'prettier', 'import'],
  settings: {
    'import/resolver': {
      typescript: {},
    },
  },
  rules: {
    // sloppy code
    'no-console': 'warn',
    'no-lonely-if': 'warn',

    // unused code
    'no-empty': 'warn',
    'require-await': 'warn',

    // style
    'curly': 'warn',
    'lines-between-class-members': 'warn',
    'no-var': 'warn',
    'object-shorthand': 'warn',
    'prefer-const': 'warn',

    '@typescript-eslint/array-type': [
      'warn',
      {
        default: 'generic',
        readonly: 'generic',
      },
    ],
    '@typescript-eslint/consistent-type-imports': [
      'warn',
      {
        // Disable to allow `typeof import('@/components/ui/BaseH5.vue')['default']`
        disallowTypeAnnotations: false,
      },
    ],

    'prettier/prettier': 'warn',

    'import/extensions': [
      'warn',
      'always',
      {
        '': 'never',
        'js': 'never',
        'jsx': 'never',
        'ts': 'never',
        'tsx': 'never',
      },
    ],
    'import/newline-after-import': 'warn',
    'import/no-duplicates': 'warn',
  },

  overrides: [
    {
      files: ['*.vue'],

      parser: 'vue-eslint-parser',
      parserOptions: {
        parser: '@typescript-eslint/parser',
        sourceType: 'module',
      },
      extends: [
        'plugin:vue/vue3-recommended',

        // Declared again to override `vue/vue3-recommended`
        'plugin:prettier/recommended',
      ],
      plugins: ['vue'],
      // add your custom rules here
      rules: {
        // Disabled because of false positives with multiple `<script>`
        'import/no-duplicates': 'off',

        // sloppy code
        'vue/multi-word-component-names': 'warn',

        // unused code
        'vue/no-empty-component-block': 'warn',
        'vue/no-unused-components': 'warn',
        'vue/no-useless-mustaches': 'warn',
        'vue/no-useless-v-bind': 'warn',

        // style
        'vue/v-on-function-call': ['warn', 'never'],
        'vue/component-definition-name-casing': ['warn', 'PascalCase'],
        'vue/component-name-in-template-casing': [
          'warn',
          'PascalCase',
          { registeredComponentsOnly: false },
        ],
        'vue/component-tags-order': [
          'warn',
          {
            order: [
              'template',
              'route',
              'script:not([setup])',
              'script[setup]',
              'style',
            ],
          },
        ],
        'vue/no-duplicate-attr-inheritance': ['warn'],
        'vue/padding-line-between-blocks': ['warn'],
        'vue/v-for-delimiter-style': ['warn', 'of'],
      },

      overrides: [
        {
          files: ['**/src/layouts/**/*.vue', '**/src/pages/**/*.vue'],
          rules: {
            'vue/multi-word-component-names': 'off',
          },
        },
      ],
    },
  ],
}