Closed angrybacon closed 1 month ago
The way I understand the issue, prettier.el infers the parser from the major mode, and markdown-mode.el doesn't have a different major mode for MDX. That means prettier.el doesn't have an easy to tell them apart. I'm surprised this was not mentioned in past issues though.
Yes, I regret that decision. It was discussed previously but I don't think there's a point in digging it up now.
What would you say is the right way to fix this, if not a different major mode for MDX files?
Have a look at prettier-major-mode-parsers
, specifically at the definitions for js*-mode
and prettier--guess-js-ish
.
Basically the workaround here is to map markdown
mode to a function such as prettier--markdown-parser
which would check the file extension and return either markdown
or mdx
depending.
FWIW I'm working (on and off) on a successor to this package that will do what you'd expect here.
Thank you for the suggestion, posting here for posterity:
(defun me/prettier-markdown-parser ()
"Return the Prettier parser for the current file.
Use the current buffer file extension if possible or fallback to the default
Markdown parser."
(if-let* ((name (buffer-file-name))
(extension (file-name-extension name))
(_ (string-equal extension "mdx")))
'(mdx)
'(markdown)))
(use-package prettier
:config
(add-to-list
'prettier-major-mode-parsers
`(markdown-mode . ,#'me/prettier-markdown-parser))
:init
(add-to-list 'safe-local-eval-forms '(prettier-mode)))
FWIW I'm working (on and off) on a successor to this package that will do what you'd expect here.
Will it compete with radian-software/apheleia or did you mean something else?
Ah, glad you worked it out, thanks for sharing your solution!
Nothing much to do with apheleia but, like apheleia, it will support other formatting tools besides Prettier. It'll also be even faster than this package, more debuggable and extensible, and fix a number of annoyances (mostly the one that made you open this ticket). I'm quite excited about it but realistically I won't be able to give it the final push before Sep/Oct. Follow me for updates or subscribe to this repo, I'll add a pointer somewhere here when it's ready.
Describe the bug
I'm making assumptions with the current title, feel free to rename appropriately. Prettier.el doesn't seem to use the same parser that
pnpx prettier
does.
```el (:emacs-version "GNU Emacs 30.0.60 (build 1, aarch64-apple-darwin23.5.0, NS appkit-2487.60 Version 14.5 (Build 23F79))\n of 2024-07-05" :prettier-el-version "1.3.0" :buffer-file-name "/path/to/project/File.mdx" :remote-id nil :major-mode markdown-mode :exec-path ("/Users/angrybacon/.nvm/versions/node/v20.12.2/bin" "/usr/local/sbin" "/opt/homebrew/bin" "/opt/homebrew/sbin" "/Users/angrybacon/Scripts" "/usr/local/bin" "/System/Cryptexes/App/usr/bin" "/Users/angrybacon/.cargo/bin" "/Users/angrybacon/Library/Android/sdk/emulator" "/Users/angrybacon/Library/Android/sdk/platform-tools" "/Users/angrybacon/.maestro/bin" "/usr/bin" "/bin" "/usr/sbin" "/sbin" "/opt/homebrew/Cellar/emacs-plus@30/30.0.60/libexec/emacs/30.0.60/aarch64-apple-darwin23.5.0") :env ("TERM=dumb" "LANG=en_US.UTF-8" "XPC_FLAGS=0x0" "XPC_SERVICE_NAME=application.org.gnu.Emacs.65335689.65335692" "__CF_USER_TEXT_ENCODING=0x1F6:0x0:0x0" "TMPDIR=/var/folders/c5/n35jrw5x4c57mbg_mzgtd6680000gp/T/" "SHELL=/bin/zsh" "HOME=/Users/angrybacon" "SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.bNSJbZZf5r/Listeners" "LOGNAME=angrybacon" "PATH=/Users/angrybacon/.nvm/versions/node/v20.12.2/bin:/usr/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/angrybacon/Scripts:/usr/local/bin:/System/Cryptexes/App/usr/bin:/Users/angrybacon/.cargo/bin:/Users/angrybacon/Library/Android/sdk/emulator:/Users/angrybacon/Library/Android/sdk/platform-tools:/Users/angrybacon/.maestro/bin:/usr/bin:/bin:/usr/sbin:/sbin" "__CFBundleIdentifier=org.gnu.Emacs" "COMMAND_MODE=unix2003" "USER=angrybacon") :prettier-options (:versions (:node "20.12.2" :acorn "8.11.3" :ada "2.7.6" :ares "1.27.0" :base64 "0.5.2" :brotli "1.1.0" :cjs_module_lexer "1.2.2" :cldr "44.1" :icu "74.2" :llhttp "8.1.2" :modules "115" :napi "9" :nghttp2 "1.60.0" :nghttp3 "0.7.0" :ngtcp2 "0.8.1" :openssl "3.0.13+quic" :simdutf "4.0.8" :tz "2024a" :undici "5.28.4" :unicode "15.1" :uv "1.46.0" :uvwasi "0.0.20" :v8 "11.3.244.8-node.19" :zlib "1.3.0.1-motley-40e35a7" :prettier "3.3.2") :options (:proseWrap "always" :singleQuote t :plugins [(:languages [(:linguistLanguageId 50 :name "CSS" :type "markup" :tmScope "source.css" :aceMode "css" :codemirrorMode "css" :codemirrorMimeType "text/css" :color "#563d7c" :extensions [".css" ".wxss"] :parsers ["css"] :vscodeLanguageIds ["css"]) (:linguistLanguageId 262764437 :name "PostCSS" :type "markup" :color "#dc3a0c" :tmScope "source.postcss" :group "CSS" :extensions [".pcss" ".postcss"] :aceMode "text" :parsers ["css"] :vscodeLanguageIds ["postcss"]) (:linguistLanguageId 198 :name "Less" :type "markup" :color "#1d365d" :aliases ["less-css"] :extensions [".less"] :tmScope "source.css.less" :aceMode "less" :codemirrorMode "css" :codemirrorMimeType "text/css" :parsers ["less"] :vscodeLanguageIds ["less"]) (:linguistLanguageId 329 :name "SCSS" :type "markup" :color "#c6538c" :tmScope "source.css.scss" :aceMode "scss" :codemirrorMode "css" :codemirrorMimeType "text/x-scss" :extensions [".scss"] :parsers ["scss"] :vscodeLanguageIds ["scss"]) (:linguistLanguageId 139 :name "GraphQL" :type "data" :color "#e10098" :extensions [".graphql" ".gql" ".graphqls"] :tmScope "source.graphql" :aceMode "text" :parsers ["graphql"] :vscodeLanguageIds ["graphql"]) (:linguistLanguageId 155 :name "Handlebars" :type "markup" :color "#f7931e" :aliases ["hbs" "htmlbars"] :extensions [".handlebars" ".hbs"] :tmScope "text.html.handlebars" :aceMode "handlebars" :parsers ["glimmer"] :vscodeLanguageIds ["handlebars"]) (:linguistLanguageId 146 :name "Angular" :type "markup" :tmScope "text.html.basic" :aceMode "html" :codemirrorMode "htmlmixed" :codemirrorMimeType "text/html" :color "#e34c26" :aliases ["xhtml"] :extensions [".component.html"] :parsers ["angular"] :vscodeLanguageIds ["html"] :filenames []) (:linguistLanguageId 146 :name "HTML" :type "markup" :tmScope "text.html.basic" :aceMode "html" :codemirrorMode "htmlmixed" :codemirrorMimeType "text/html" :color "#e34c26" :aliases ["xhtml"] :extensions [".html" ".hta" ".htm" ".html.hl" ".inc" ".xht" ".xhtml" ".mjml"] :parsers ["html"] :vscodeLanguageIds ["html"]) (:linguistLanguageId 146 :name "Lightning Web Components" :type "markup" :tmScope "text.html.basic" :aceMode "html" :codemirrorMode "htmlmixed" :codemirrorMimeType "text/html" :color "#e34c26" :aliases ["xhtml"] :extensions [] :parsers ["lwc"] :vscodeLanguageIds ["html"] :filenames []) (:linguistLanguageId 391 :name "Vue" :type "markup" :color "#41b883" :extensions [".vue"] :tmScope "text.html.vue" :aceMode "html" :parsers ["vue"] :vscodeLanguageIds ["vue"]) (:linguistLanguageId 183 :name "JavaScript" :type "programming" :tmScope "source.js" :aceMode "javascript" :codemirrorMode "javascript" :codemirrorMimeType "text/javascript" :color "#f1e05a" :aliases ["js" "node"] :extensions [".js" "._js" ".bones" ".cjs" ".es" ".es6" ".frag" ".gs" ".jake" ".javascript" ".jsb" ".jscad" ".jsfl" ".jslib" ".jsm" ".jspre" ".jss" ".mjs" ".njs" ".pac" ".sjs" ".ssjs" ".xsjs" ".xsjslib" ".wxs"] :filenames ["Jakefile"] :interpreters ["chakra" "d8" "gjs" "js" "node" "nodejs" "qjs" "rhino" "v8" "v8-shell" "zx"] :parsers ["babel" "acorn" "espree" "meriyah" "babel-flow" "babel-ts" "flow" "typescript"] :vscodeLanguageIds ["javascript" "mongo"]) (:linguistLanguageId 183 :name "Flow" :type "programming" :tmScope "source.js" :aceMode "javascript" :codemirrorMode "javascript" :codemirrorMimeType "text/javascript" :color "#f1e05a" :aliases [] :extensions [".js.flow"] :filenames [] :interpreters ["chakra" "d8" "gjs" "js" "node" "nodejs" "qjs" "rhino" "v8" "v8-shell"] :parsers ["flow" "babel-flow"] :vscodeLanguageIds ["javascript"]) (:linguistLanguageId 183 :name "JSX" :type "programming" :tmScope "source.js.jsx" :aceMode "javascript" :codemirrorMode "jsx" :codemirrorMimeType "text/jsx" :extensions [".jsx"] :parsers ["babel" "babel-flow" "babel-ts" "flow" "typescript" "espree" "meriyah"] :vscodeLanguageIds ["javascriptreact"] :group "JavaScript") (:linguistLanguageId 378 :name "TypeScript" :type "programming" :color "#3178c6" :aliases ["ts"] :interpreters ["deno" "ts-node"] :extensions [".ts" ".cts" ".mts"] :tmScope "source.ts" :aceMode "typescript" :codemirrorMode "javascript" :codemirrorMimeType "application/typescript" :parsers ["typescript" "babel-ts"] :vscodeLanguageIds ["typescript"]) (:linguistLanguageId 94901924 :name "TSX" :type "programming" :color "#3178c6" :group "TypeScript" :extensions [".tsx"] :tmScope "source.tsx" :aceMode "javascript" :codemirrorMode "jsx" :codemirrorMimeType "text/jsx" :parsers ["typescript" "babel-ts"] :vscodeLanguageIds ["typescriptreact"]) (:linguistLanguageId 174 :name "JSON.stringify" :type "data" :color "#292929" :tmScope "source.json" :aceMode "json" :codemirrorMode "javascript" :codemirrorMimeType "application/json" :aliases ["geojson" "jsonl" "topojson"] :extensions [".importmap"] :filenames ["package.json" "package-lock.json" "composer.json"] :parsers ["json-stringify"] :vscodeLanguageIds ["json"]) (:linguistLanguageId 174 :name "JSON" :type "data" :color "#292929" :tmScope "source.json" :aceMode "json" :codemirrorMode "javascript" :codemirrorMimeType "application/json" :aliases ["geojson" "jsonl" "topojson"] :extensions [".json" ".4DForm" ".4DProject" ".avsc" ".geojson" ".gltf" ".har" ".ice" ".JSON-tmLanguage" ".mcmeta" ".tfstate" ".tfstate.backup" ".topojson" ".webapp" ".webmanifest" ".yy" ".yyp"] :filenames [".all-contributorsrc" ".arcconfig" ".auto-changelog" ".c8rc" ".htmlhintrc" ".imgbotconfig" ".nycrc" ".tern-config" ".tern-project" ".watchmanconfig" "Pipfile.lock" "composer.lock" "flake.lock" "mcmod.info" ".babelrc" ".jscsrc" ".jshintrc" ".jslintrc" ".swcrc"] :parsers ["json"] :vscodeLanguageIds ["json"]) (:linguistLanguageId 423 :name "JSON with Comments" :type "data" :color "#292929" :group "JSON" :tmScope "source.js" :aceMode "javascript" :codemirrorMode "javascript" :codemirrorMimeType "text/javascript" :aliases ["jsonc"] :extensions [".jsonc" ".code-snippets" ".code-workspace" ".sublime-build" ".sublime-commands" ".sublime-completions" ".sublime-keymap" ".sublime-macro" ".sublime-menu" ".sublime-mousemap" ".sublime-project" ".sublime-settings" ".sublime-theme" ".sublime-workspace" ".sublime_metrics" ".sublime_session"] :filenames [] :parsers ["jsonc"] :vscodeLanguageIds ["jsonc"]) (:linguistLanguageId 175 :name "JSON5" :type "data" :color "#267CB9" :extensions [".json5"] :tmScope "source.js" :aceMode "javascript" :codemirrorMode "javascript" :codemirrorMimeType "application/json" :parsers ["json5"] :vscodeLanguageIds ["json5"]) (:linguistLanguageId 222 :name "Markdown" :type "prose" :color "#083fa1" :aliases ["md" "pandoc"] :aceMode "markdown" :codemirrorMode "gfm" :codemirrorMimeType "text/x-gfm" :wrap t :extensions [".md" ".livemd" ".markdown" ".mdown" ".mdwn" ".mkd" ".mkdn" ".mkdown" ".ronn" ".scd" ".workbook"] :filenames ["contents.lr" "README"] :tmScope "text.md" :parsers ["markdown"] :vscodeLanguageIds ["markdown"]) (:linguistLanguageId 222 :name "MDX" :type "prose" :color "#083fa1" :aliases ["md" "pandoc"] :aceMode "markdown" :codemirrorMode "gfm" :codemirrorMimeType "text/x-gfm" :wrap t :extensions [".mdx"] :filenames [] :tmScope "text.md" :parsers ["mdx"] :vscodeLanguageIds ["mdx"]) (:linguistLanguageId 407 :name "YAML" :type "data" :color "#cb171e" :tmScope "source.yaml" :aliases ["yml"] :extensions [".yml" ".mir" ".reek" ".rviz" ".sublime-syntax" ".syntax" ".yaml" ".yaml-tmlanguage" ".yaml.sed" ".yml.mysql"] :filenames [".clang-format" ".clang-tidy" ".gemrc" "CITATION.cff" "glide.lock" ".prettierrc" ".stylelintrc" ".lintstagedrc"] :aceMode "yaml" :codemirrorMode "yaml" :codemirrorMimeType "text/x-yaml" :parsers ["yaml"] :vscodeLanguageIds ["yaml" "ansible" "home-assistant"])] :options (:singleQuote (:category "Common" :type "boolean" :default nil :description "Use single quotes instead of double quotes.") :bracketSpacing (:category "Common" :type "boolean" :default t :description "Print spaces between brackets." :oppositeDescription "Do not print spaces between brackets.") :bracketSameLine (:category "Common" :type "boolean" :default nil :description "Put > of opening tags on the last line instead of on a new line.") :htmlWhitespaceSensitivity (:category "HTML" :type "choice" :default "css" :description "How to handle whitespaces in HTML." :choices [(:value "css" :description "Respect the default value of CSS display property.") (:value "strict" :description "Whitespaces are considered sensitive.") (:value "ignore" :description "Whitespaces are considered insensitive.")]) :singleAttributePerLine (:category "Common" :type "boolean" :default nil :description "Enforce single attribute per line in HTML, Vue and JSX.") :vueIndentScriptAndStyle (:category "HTML" :type "boolean" :default nil :description "Indent script and style tags in Vue files.") :arrowParens (:category "JavaScript" :type "choice" :default "always" :description "Include parentheses around a sole arrow function parameter." :choices [(:value "always" :description "Always include parens. Example: `(x) => x`") (:value "avoid" :description "Omit parens when possible. Example: `x => x`")]) :jsxBracketSameLine (:category "JavaScript" :type "boolean" :description "Put > on the last line instead of at a new line." :deprecated "2.4.0") :semi (:category "JavaScript" :type "boolean" :default t :description "Print semicolons." :oppositeDescription "Do not print semicolons, except at the beginning of lines which may need them.") :experimentalTernaries (:category "JavaScript" :type "boolean" :default nil :description "Use curious ternaries, with the question mark after the condition." :oppositeDescription "Default behavior of ternaries; keep question marks on the same line as the consequent.") :jsxSingleQuote (:category "JavaScript" :type "boolean" :default nil :description "Use single quotes in JSX.") :quoteProps (:category "JavaScript" :type "choice" :default "as-needed" :description "Change when properties in objects are quoted." :choices [(:value "as-needed" :description "Only add quotes around object properties where required.") (:value "consistent" :description "If at least one property in an object requires quotes, quote all properties.") (:value "preserve" :description "Respect the input use of quotes in object properties.")]) :trailingComma (:category "JavaScript" :type "choice" :default "all" :description "Print trailing commas wherever possible when multi-line." :choices [(:value "all" :description "Trailing commas wherever possible (including function arguments).") (:value "es5" :description "Trailing commas where valid in ES5 (objects, arrays, etc.)") (:value "none" :description "No trailing commas.")]) :proseWrap (:category "Common" :type "choice" :default "preserve" :description "How to wrap prose." :choices [(:value "always" :description "Wrap prose if it exceeds the print width.") (:value "never" :description "Do not wrap prose.") (:value "preserve" :description "Wrap prose as-is.")])) :parsers (:markdown (:astFormat "mdast") :mdx (:astFormat "mdast") :remark (:astFormat "mdast") :css (:astFormat "postcss") :less (:astFormat "postcss") :scss (:astFormat "postcss")) :printers (:estree (:experimentalFeatures (:avoidAstMutation t) :handleComments nil) :estree-json nil :mdast nil :postcss nil)) (:parsers (:prettier-el-null-parser (:astFormat "estree"))) (:name "/path/to/project/node_modules/@ianvs/prettier-plugin-sort-imports/lib/src/index.js" :options (:importOrder (:type "path" :category "Global" :array t :default [(:value ["M-x prettier-info
To Reproduce
Write a
File.mdx
file withRun
pnpx prettier -w File.mdx
Confirm that
File.mdx
is already well-formattedRun
prettier-prettify
Confirm that
File.mdx
is formatted using different heuristics (wrong ones too)Note that the first import statement is split over 2 lines, like regular text from Markdown would.
Steps to reproduce the behavior.
Expected behavior
I'd expect
pnpx prettier -w
andprettier-prettify
to format my file with the same heuristics.Additional context
The way I understand the issue,
prettier.el
infers the parser from the major mode, andmarkdown-mode.el
doesn't have a different major mode for MDX. That meansprettier.el
doesn't have an easy to tell them apart. I'm surprised this was not mentioned in past issues though.What would you say is the right way to fix this, if not a different major mode for MDX files?