Summary
prettier-pre-warm documents none as "No pre-warming, everything on-demand", and its docstring says that with none "you tend to wait for it on first save". That doesn't hold while prettier-mode-sync-config-flag is at its default of t: enabling prettier-mode still starts the Node server and blocks on a synchronous round-trip, because the config-sync path starts the process independently of the pre-warm setting.
This was quite noticeable when I had desktop-save-mode restoring buffers across ~10 TypeScript projects. Startup does one blocking round-trip per restored buffer, each flashing "Prettier syncing config" in the echo area, even with prettier-pre-warm set to none specifically to avoid paying that cost at startup.
Note: I'm willing to create the PR for this.
Possibly related: #81 ("Pre-warm idea").
Environment
- prettier.el 20240902.1516 (MELPA)
- GNU Emacs 30.2, macOS (aarch64)
prettier-pre-warm set to none, prettier-mode-sync-config-flag left at its default t
The code path
The two branches in prettier-mode are independent:
(if prettier-mode
(progn
(unless (eq prettier-pre-warm 'none)
(prettier--get-process
(eq prettier-pre-warm 'full)))
(when prettier-mode-sync-config-flag
(prettier--maybe-sync-config)
...
With pre-warm set to none the first branch is correctly skipped, but the second one runs prettier--maybe-sync-config → prettier--load-config-cached → prettier--load-config, and that function calls prettier--get-process itself:
(defun prettier--load-config (&optional override-parsers)
...
(with-temp-message "Prettier syncing config"
(let* ((start-time (current-time))
(prettier-process (prettier--get-process))
...
So the server starts and is waited on regardless of prettier-pre-warm.
Worth noting that prettier--load-config-cached only caches for buffers not visiting a file; file-visiting buffers take the (buffer-file-name) branch and call prettier--load-config unconditionally. Ten files in one project therefore cost ten round-trips, not one, even though they resolve to the same Prettier config.
Reproduction
(setq prettier-pre-warm 'none)
- Open a
.ts file in a project with a Prettier config and enable prettier-mode.
- Observe "Prettier syncing config" and a live
*prettier (local)* process, before any edit or save.
Expected per the docstring: no server, no wait, until the first save.
Suggested fix
Rather than adding another option, let the existing prettier-pre-warm govern the config sync as well:
full / some: sync at prettier-mode activation, as today.
none: defer it, e.g. via a one-shot buffer-local first-change-hook, so the settings land before the user's first edit rather than at mode activation.
That keeps the documented meaning of none ("everything on-demand") true, adds no new user-facing surface, and preserves the sync. The sync is what derives indent_style / indent_size / max_line_length via editorconfig-set-local-variables for projects that have a .prettierrc but no .editorconfig, and what sets the coding system from endOfLine.
Caveat: this is a mild behavior change for anyone currently running none and relying on the sync happening at activation. If that's a concern it could be a distinct prettier-mode-sync-config-flag value (e.g. on-first-change) instead, leaving t untouched. Happy to send a PR for whichever shape you prefer.
Workaround
For anyone hitting the same startup stall, deferring the sync from outside the package:
(setq prettier-pre-warm 'none
prettier-mode-sync-config-flag nil)
(defun my-prettier-sync-config-now (buffer)
(when (buffer-live-p buffer)
(with-current-buffer buffer
(when (and prettier-mode (fboundp 'prettier--maybe-sync-config))
;; `prettier--maybe-sync-config' checks the flag itself.
(let ((prettier-mode-sync-config-flag t))
(prettier--maybe-sync-config))))))
(defun my-prettier-sync-config-on-first-change ()
"Sync this buffer's Prettier config, once, when it is first edited.
Runs off an idle timer rather than inline: `first-change-hook' fires
from inside `prepare-to-modify-buffer', which is no place for a
blocking round-trip to the Prettier server."
(remove-hook 'first-change-hook #'my-prettier-sync-config-on-first-change 'local)
(run-with-idle-timer 0 nil #'my-prettier-sync-config-now (current-buffer)))
(add-hook 'prettier-mode-hook
(lambda ()
(when prettier-mode
(add-hook 'first-change-hook
#'my-prettier-sync-config-on-first-change
nil 'local))))
Summary
prettier-pre-warmdocumentsnoneas "No pre-warming, everything on-demand", and its docstring says that withnone"you tend to wait for it on first save". That doesn't hold whileprettier-mode-sync-config-flagis at its default oft: enablingprettier-modestill starts the Node server and blocks on a synchronous round-trip, because the config-sync path starts the process independently of the pre-warm setting.This was quite noticeable when I had
desktop-save-moderestoring buffers across ~10 TypeScript projects. Startup does one blocking round-trip per restored buffer, each flashing "Prettier syncing config" in the echo area, even withprettier-pre-warmset tononespecifically to avoid paying that cost at startup.Note: I'm willing to create the PR for this.
Possibly related: #81 ("Pre-warm idea").
Environment
prettier-pre-warmset tonone,prettier-mode-sync-config-flagleft at its defaulttThe code path
The two branches in
prettier-modeare independent:With
pre-warmset tononethe first branch is correctly skipped, but the second one runsprettier--maybe-sync-config→prettier--load-config-cached→prettier--load-config, and that function callsprettier--get-processitself:So the server starts and is waited on regardless of
prettier-pre-warm.Worth noting that
prettier--load-config-cachedonly caches for buffers not visiting a file; file-visiting buffers take the(buffer-file-name)branch and callprettier--load-configunconditionally. Ten files in one project therefore cost ten round-trips, not one, even though they resolve to the same Prettier config.Reproduction
(setq prettier-pre-warm 'none).tsfile in a project with a Prettier config and enableprettier-mode.*prettier (local)*process, before any edit or save.Expected per the docstring: no server, no wait, until the first save.
Suggested fix
Rather than adding another option, let the existing
prettier-pre-warmgovern the config sync as well:full/some: sync atprettier-modeactivation, as today.none: defer it, e.g. via a one-shot buffer-localfirst-change-hook, so the settings land before the user's first edit rather than at mode activation.That keeps the documented meaning of
none("everything on-demand") true, adds no new user-facing surface, and preserves the sync. The sync is what derivesindent_style/indent_size/max_line_lengthviaeditorconfig-set-local-variablesfor projects that have a.prettierrcbut no.editorconfig, and what sets the coding system fromendOfLine.Caveat: this is a mild behavior change for anyone currently running
noneand relying on the sync happening at activation. If that's a concern it could be a distinctprettier-mode-sync-config-flagvalue (e.g.on-first-change) instead, leavingtuntouched. Happy to send a PR for whichever shape you prefer.Workaround
For anyone hitting the same startup stall, deferring the sync from outside the package: