sveltejs / prettier-plugin-svelte

Format your svelte components using prettier.
MIT License
735 stars 95 forks source link

A <textarea> can have either a value attribute or (equivalently) child content, but not boths #458

Open craigcosmo opened 2 weeks ago

craigcosmo commented 2 weeks ago

I got this error, how can I disable it ?

CleanShot 2024-08-29 at 00 47 29 CleanShot 2024-08-29 at 00 47 39

and this is my prettierrc

{
    "useTabs": true,
    "singleQuote": true,
    "semi": false,
    "printWidth": 70,
    "bracketSameLine": true,
    "htmlWhitespaceSensitivity": "ignore",
    "plugins": [
        "prettier-plugin-svelte",
        "prettier-plugin-tailwindcss"
    ],
    "overrides": [
        {
            "files": "*.svelte",
            "options": { "parser": "svelte" }
        }
    ]
}
dummdidumm commented 2 weeks ago

Please provide a code snippet and/or reproduction link (Stackblitz or GitHub). Just from looking at the screenshot there's not much we can tell

craigcosmo commented 2 weeks ago

the code snippet

<textarea
    on:keydown={onKeyDown}
    placeholder="put a bunch of series here"
    bind:value={text}>
</textarea>

package json

"devDependencies": {
        "@silintl/svelte-google-places-autocomplete": "^1.2.5",
        "@sveltejs/adapter-auto": "^3.2.3",
        "@sveltejs/adapter-node": "^5.2.1",
        "@sveltejs/kit": "^2.5.21",
        "@sveltejs/vite-plugin-svelte": "^3.1.1",
        "@types/bun": "^1.1.6",
        "@types/js-cookie": "^3.0.6",
        "@types/store": "^2.0.5",
        "@typescript-eslint/eslint-plugin": "^8.0.1",
        "@typescript-eslint/parser": "^8.0.1",
        "autoprefixer": "^10.4.20",
        "cheerio": "^1.0.0-rc.12",
        "cross-blob": "^3.0.2",
        "eslint": "^9.9.0",
        "eslint-config-prettier": "^9.1.0",
        "eslint-plugin-svelte": "^2.43.0",
        "fzf": "^0.5.2",
        "js-cookie": "^3.0.5",
        "moment": "^2.30.1",
        "moment-timezone": "^0.5.45",
        "nprogress": "^0.2.0",
        "postcss": "^8.4.41",
        "prettier": "^3.3.3",
        "prettier-plugin-svelte": "^3.2.6",
        "prisma": "5.18.0",
        "sass": "^1.77.8",
        "socket.io-client": "^4.7.5",
        "store": "^2.0.12",
        "svelte-autosize": "^1.1.0",
        "svelte-check": "^3.8.5",
        "svelte-dnd-action": "^0.9.49",
        "svelte-file-dropzone": "^2.0.7",
        "tailwindcss": "^3.4.9",
        "tslib": "^2.6.3",
        "typescript": "^5.5.4",
        "uniqid": "^5.4.0",
        "vite": "^5.4.0",
        "vitest": "^2.0.5"
    },
    "dependencies": {
        "@prisma/client": "5.18.0",
        "@svelte-plugins/tooltips": "^3.0.1",
        "@tailwindcss/aspect-ratio": "^0.4.2",
        "@tailwindcss/forms": "^0.5.7",
        "@tailwindcss/line-clamp": "^0.4.4",
        "@types/lodash-es": "^4.17.12",
        "@types/valid-url": "^1.0.7",
        "@zerodevx/svelte-toast": "^0.9.5",
        "basic-ftp": "^5.0.5",
        "caniuse-lite": "^1.0.30001651",
        "component-emitter": "^2.0.0",
        "fontsome": "^1.0.5",
        "fuse.js": "^7.0.0",
        "googleapis": "^140.0.1",
        "knex": "^3.1.0",
        "lodash-es": "^4.17.21",
        "mysql": "^2.18.1",
        "mysql2": "^3.11.0",
        "nodemailer": "^6.9.14",
        "nodemailer-express-handlebars": "^6.1.2",
        "path": "^0.12.7",
        "pg": "^8.12.0",
        "prettier-plugin-tailwindcss": "^0.6.6",
        "sharp": "^0.33.4",
        "svelte": "^4.2.18",
        "svelte-adapter-bun": "^0.5.2",
        "svelte-flag-icons": "^1.0.3",
        "svelte-select": "^5.8.3",
        "sveltekit-barcode": "^2.0.8",
        "valid-url": "^1.0.9"
    }

prettierrc

{
    "useTabs": true,
    "singleQuote": true,
    "semi": false,
    "printWidth": 70,
    "bracketSameLine": true,
    "htmlWhitespaceSensitivity": "ignore",
    "plugins": [
        "prettier-plugin-svelte",
        "prettier-plugin-tailwindcss"
    ],
    "overrides": [
        {
            "files": "*.svelte",
            "options": { "parser": "svelte" }
        }
    ]
}

viteconfig


import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vitest/config'

export default defineConfig({
    plugins: [sveltekit()],
    test: {
        include: ['src/**/*.{test,spec}.{js,ts}']
    }
})

svelte config

import adapter from 'svelte-adapter-bun'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: vitePreprocess(),
    onwarn: (warning, handler) => {
        if (warning.code.startsWith('a11y-')) {
            return
        }
        if (warning.code.startsWith('css-unused-selector')) {
            return
        }
        handler(warning)
    },
    kit: {
        adapter: adapter(),
    },
}

export default config

eslintrc.js

module.exports = {
    root: true,
    extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/recommended',
        'plugin:svelte/recommended',
        'prettier',
    ],
    parser: '@typescript-eslint/parser',
    plugins: ['@typescript-eslint'],
    parserOptions: {
        sourceType: 'module',
        ecmaVersion: 2020,
        extraFileExtensions: ['.svelte'],
    },
    env: {
        browser: true,
        es2017: true,
        node: true,
    },
    overrides: [
        {
            files: ['*.svelte'],
            parser: 'svelte-eslint-parser',
            parserOptions: {
                parser: '@typescript-eslint/parser',
            },
        },
    ],
    ignorePatterns: ['*.cjs'],
    rules: {
        'svelte/no-at-html-tags': 0,
        'svelte/valid-compile': ['error', { ignoreWarnings: true }],
        '@typescript-eslint/ban-ts-comment': 0,
        '@typescript-eslint/no-inferrable-types': 0,
        'a11y-no-static-element-interactions': 0,
        'no-var': 0,
        'prefer-const': 0,
        '@typescript-eslint/no-explicit-any': 0,
        '@typescript-eslint/no-unused-vars': 0,
        '@typescript-eslint/no-empty-function': 0,
        'a11y-label-has-associated-control': 0,
        'a11y-missing-attribute': 0,
        'a11y-click-events-have-key-events': 0,
        'no-constant-condition': 0,
        'no-prototype-builtins': 0,
        'a11y-autocomplete-valid': 0,
        'no-empty': 0,
    },
}
dummdidumm commented 2 weeks ago

This is a combination of your whitespaceSensitivity: "none" setting in your prettier config and the fact that whitespace inside textarea is important. Our Prettier plugin therefore formats the textarea closing block into a separate line. That does not work because whitespace is actually important within textareas - well, almost, except for the first line, which is ignored.

I think the best fix would be to handle this special case within the Svelte parser and not error in this case. Given that this is such an edge case, we're likely only going to fix this in Svelte 5. You can easily work around it by adding a <!-- prettier-ignore --> comment at the top of your textarea element and make sure there's no space between the opening and closing tag:

<!-- prettier-ignore -->
<textarea
    on:keydown={onKeyDown}
    placeholder="put a bunch of series here"
    bind:value={text}
></textarea>

I'm also wondering if we should align our formatting with that of Prettier's HTML formatting, i.e. don't force an empty line in this case, instead format it like in the code snippet above.