prettier / plugin-pug

Prettier Pug Plugin
https://prettier.github.io/plugin-pug
MIT License
198 stars 44 forks source link

Possibility to transfer it nicely when using tailwindcss #413

Open vcharov opened 2 years ago

vcharov commented 2 years ago

Request / Idea

It would be nice to be able to transfer it nicely when using tailwindcss. This would make the code much more readable

Input

MenuButton(
  class=["relative z-10 flex items-center w-11 gap-2 whitespace-nowrap text-ellipsis overflow-hidden text-xs", "lg:gap-6"]
)

Expected Output

MenuButton(
  class=[
    "relative z-10 flex items-center w-11 gap-2 whitespace-nowrap text-ellipsis overflow-hidden text-xs",
    "lg:gap-6"
  ]
)
Shinigami92 commented 2 years ago

Is it expected / correct that you didn't use quotes but class=[ ... (instead of class="[ ...) If yes I'm afraid you are running into the same problem as many other users did before you => https://github.com/prettier/plugin-pug/issues/66

This is a limitation of pug itself and is currently not really handleable 😔

vcharov commented 2 years ago

you can write it that way, but it looks strange

MenuButton(
  class={
    "relative z-10 flex items-center w-11 gap-2 whitespace-nowrap text-ellipsis overflow-hidden text-xs": true,
    "lg:gap-6": true
  }
)
Shinigami92 commented 2 years ago

class={ results in the same issue It's the parser that has the problem, not the formatter

vcharov commented 2 years ago

Maybe there is a way not to make a fix for these cases?

header.header-module(
  class=[
    "bg-off-white shadow-main-header flex items-center gap-x-2 py-2 px-4",
    "lg:px-8"
  ]
)

Снимок 2022-09-08 17 45 12

Shinigami92 commented 2 years ago

I'm open to PRs Please open a PR and add a test, you can orientate yourself on any test inside https://github.com/prettier/plugin-pug/tree/main/tests/issues Happy to review your solution

vcharov commented 2 years ago

Did I understand you correctly, there is no such rule at the moment?

Shinigami92 commented 2 years ago

Did I understand you correctly, there is no such rule at the moment?

You are in the repo for the pug formatter A formatter doesn't have rules, but options The rule that got triggered in your screenshot is coming from https://www.npmjs.com/package/eslint-plugin-prettier-vue

vcharov commented 2 years ago

thanks for the help

vcharov commented 2 years ago

Please advise where is the parsing code

Shinigami92 commented 2 years ago

Please have a look into this: https://github.com/pugjs/pug/issues/3214

vcharov commented 2 years ago

The point is that the parser works correctly, the expected code is valid and works correctly. But plugin:prettier-vue/recommended prohibits such hyphenation

package.json

{
  "name": "spa",
  "version": "0.0.1",
  "main": "src/main.ts",
  "scripts": {
    "watch": "vite --host handlarfinans.local --port 8888",
    "dev": "vue-tsc --noEmit && vite build",
    "prod": "vue-tsc --noEmit && vite build",
    "lint": "echo null",
    "lint:disabled": "npm run lint:js",
    "lint:fix": "npm run lint:js -- --fix",
    "lint:js": "cross-env NODE_ENV=production eslint --ext .js,.vue --ignore-path .gitignore src"
  },
  "dependencies": {
    "@bundled-es-modules/axios": "^0.27.2",
    "@headlessui/vue": "^1.6.7",
    "@vueuse/head": "^0.7.9",
    "axios": "^0.27.2",
    "pinia": "^2.0.18",
    "vue": "^3.2.37",
    "vue-i18n": "^9.2.2",
    "vue-router": "^4.1.3",
    "vue3-mq": "^3.1.0"
  },
  "devDependencies": {
    "@prettier/plugin-pug": "^2.2.0",
    "@tailwindcss/aspect-ratio": "^0.4.0",
    "@tailwindcss/forms": "^0.5.2",
    "@tailwindcss/line-clamp": "^0.4.0",
    "@tailwindcss/typography": "^0.5.4",
    "@typescript-eslint/eslint-plugin": "^5.32.0",
    "@typescript-eslint/parser": "^5.33.0",
    "@vitejs/plugin-vue": "^3.0.0",
    "autoprefixer": "^10.4.8",
    "cross-env": "^7.0.3",
    "eslint": "^8.21.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier-vue": "^4.2.0",
    "eslint-plugin-security": "^1.5.0",
    "eslint-plugin-tailwindcss": "^3.6.0",
    "eslint-plugin-vue": "^9.3.0",
    "eslint-plugin-vue-pug": "^0.5.4",
    "husky": "^8.0.1",
    "postcss": "^8.4.14",
    "pug": "^3.0.2",
    "stylelint": "^14.9.1",
    "stylelint-config-rational-order": "^0.1.2",
    "stylelint-config-recommended-vue": "^1.4.0",
    "stylelint-config-standard": "^26.0.0",
    "tailwind-scrollbar": "^2.0.1",
    "tailwindcss": "^3.1.8",
    "tailwindcss-debug-screens": "^2.2.1",
    "typescript": "^4.6.4",
    "unplugin-auto-import": "^0.11.1",
    "unplugin-vue-components": "^0.22.0",
    "vite": "^3.0.0",
    "vite-plugin-pages": "^0.25.0",
    "vite-plugin-svg-icons": "^2.0.1",
    "vite-plugin-vue-layouts": "0.6.0",
    "vue-eslint-parser": "^9.0.3",
    "vue-tsc": "^0.38.4"
  }
}

.eslintrc.js

module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-recommended',
    'plugin:vue-pug/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier-vue/recommended',
    'plugin:security/recommended',
    'plugin:tailwindcss/recommended',
    '.eslintrc-auto-import.json',
  ],
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaVersion: 2021,
  },
  rules: {
    //- prettier
    'prettier-vue/prettier': [
      'error',
      {
        singleQuote: true,
        semi: false,
        eol: 'lf',
        printWidth: 80,

        //- Prettier: pug
        pugPrintWidth: 80,
        pugFramework: 'vue',
        pugSingleQuote: false,
        pugAttributeSeparator: 'none',
        pugWrapAttributesThreshold: 1,
        pugEmptyAttributesForceQuotes: ['class', 'type', 'style', 'v-model'],
        pugClassNotation: 'as-is',
        pugEmptyAttributes: 'none',
      },
    ],

    //- pug
    'vue-pug/no-pug-control-flow': 'error',

    //- eslint
    'no-var': 'error',
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'comma-dangle': ['error', 'only-multiline'],
    'id-length': [2, { exceptions: ['i', 'j', '_', 't'] }],
    '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '_' }],
    'vue/block-lang': [
      'error',
      {
        template: { lang: 'pug' },
      },
    ],
    'vue/component-api-style': ['error', ['script-setup']],
    'vue/html-button-has-type': [
      'error',
      {
        button: true,
        submit: true,
        reset: true,
      },
    ],
    'vue/component-name-in-template-casing': [
      'error',
      'PascalCase',
      {
        registeredComponentsOnly: false,
      },
    ],
    'vue/custom-event-name-casing': 'error',
    'vue/define-macros-order': 'error',
    'vue/no-empty-component-block': 'error',
    'vue/no-multiple-objects-in-class': 'error',
    'vue/no-restricted-call-after-await': 'error',
    'vue/no-static-inline-styles': 'error',
    'vue/no-template-target-blank': 'error',
    'vue/no-undef-properties': 'error',
    'vue/no-unsupported-features': ['error', { version: '^3.2.37' }],
    'vue/no-useless-mustaches': 'error',
    'vue/no-useless-v-bind': 'error',
    'vue/padding-line-between-blocks': 'error',
    'vue/prefer-true-attribute-shorthand': 'error',
    'vue/require-emit-validator': 'error',
    'vue/v-for-delimiter-style': 'error',
    'vue/v-on-function-call': 'error',
    'vue/component-tags-order': [
      'error',
      { order: ['script:not([setup])', 'script[setup]', 'template', 'style'] },
    ],

    //- tailwindcss
    'tailwindcss/no-custom-classname': 'off',
    'tailwindcss/classnames-order': 'off',
  },
  globals: {
    defineProps: 'readonly',
    defineEmits: 'readonly',
    defineExpose: 'readonly',
    withDefaults: 'readonly',
  },
}
Shinigami92 commented 2 years ago

As I said: please open a PR and try to tackle this issue on your own I'm happy to review

Shinigami92 commented 2 years ago

Some hints: Please have a look at this method: https://github.com/prettier/plugin-pug/blob/d520345569c50dfa3350588bb835dce6ccf4e008/src/printer.ts#L1236 And please add a test in https://github.com/prettier/plugin-pug/tree/main/tests/issues

From there I might help further, but please first put some effort on your own into this

Shinigami92 commented 2 years ago

Home now and I looked into the code and started to debug a bit

Seems like you may have luck, I'm not promising anything but it might be you are not affected directly be the parsers problem

So around here: https://github.com/prettier/plugin-pug/blob/d520345569c50dfa3350588bb835dce6ccf4e008/src/printer.ts#L934-L935 it goes inside the first if, but it's not "quoted", so doesn't go inside the second if Now you have the token.val there and it contains the full string. It might be possible to pass this into e.g. a json or js parser as it is an array in your case.

Your example also has the following tokens:

[
    {"type":"tag","loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":11}},"val":"MenuButton"},
    {"type":"start-attributes","loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12}}},
    {"type":"attribute","loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":123}},"name":"class","val":"[\"relative z-10 flex items-center w-11 gap-2 whitespace-nowrap text-ellipsis overflow-hidden text-xs\", \"lg:gap-6\"]","mustEscape":true},
    {"type":"end-attributes","loc":{"start":{"line":3,"column":1},"end":{"line":3,"column":2}}},
    {"type":"newline","loc":{"start":{"line":4,"column":1},"end":{"line":4,"column":1}}},
    {"type":"eos","loc":{"start":{"line":4,"column":1},"end":{"line":4,"column":1}}}
]

for this example:

MenuButton(
  class="relative",
  class=["relative z-10 flex items-center w-11 gap-2 whitespace-nowrap text-ellipsis overflow-hidden text-xs", "lg:gap-6"]
)

I just added another class attribute, just to show that the first is quoted and the second (your example) is not counted as quoted

That's all of the help I can give you for the start, now good luck :)

vcharov commented 2 years ago

Thanks