diff --git a/.github/renovate.json b/.github/renovate.json index 5ced86b7..7afb4fdd 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -58,6 +58,12 @@ "matchPackageNames": ["tailwindcss", "@tailwindcss/**", "@nuxtjs/tailwindcss"], "matchUpdateTypes": ["major"], "enabled": false + }, + { + "description": "Held below 0.8.0: 0.8.x stops @ianvs/prettier-plugin-sort-imports running on .vue files and leaves imports scrambled (tailwindlabs/prettier-plugin-tailwindcss#465). Without this rule the 'linting and formatting' group above would re-propose it as a routine minor. Remove once that issue is closed and a .vue file still formats with sorted imports.", + "matchFileNames": ["nuxt-app/**"], + "matchPackageNames": ["prettier-plugin-tailwindcss"], + "allowedVersions": "<0.8.0" } ] } diff --git a/docs/dependency-upgrade-plan.md b/docs/dependency-upgrade-plan.md index 9809eb88..b42963cd 100644 --- a/docs/dependency-upgrade-plan.md +++ b/docs/dependency-upgrade-plan.md @@ -39,6 +39,7 @@ each phase leaves the app in a shippable state and can be reverted on its own. | 4 | **Nuxt 3 → Nuxt 4** | ✅ Done (2026-08-03) | | 5 | Ecosystem majors (Pinia, ESLint, Zod, Directus SDK, Stripe, DOMPurify) | 🔄 In progress — 5 of 7 done, 1 **reverted upstream**; only `stripe` left; **audit at zero** | | 6 | Deferred: Tailwind 4, Node 24 | ⬜ Deliberately deferred | +| — | [Registry sweep and the formatter bump](#registry-sweep-and-the-formatter-bump--done-2026-08-04) | ✅ Done (2026-08-04) | | — | [After the plan — follow-up backlog](#after-the-plan--follow-up-backlog) | 📋 Consolidated, unscheduled | Each phase's own write-up ends with what it deliberately left behind. Those are also gathered into @@ -1428,6 +1429,98 @@ needed Directus write access or a tampered Algolia index. This was defence in de and the strongest argument for fixing it was never the exploit, it was that four components were pushing plain text through an HTML sink for no reason. +## Registry sweep and the formatter bump — done 2026-08-04 + +Asked whether anything besides `stripe` was still behind, and answered against the npm registry +rather than against this document. **Four things were, and one of them appeared in no phase at all.** + +| package | installed | latest | status | +| --- | --- | --- | --- | +| `stripe` | 20.4.1 | 22.4.0 | ⬜ the last Phase 5 item, waiting on a colleague | +| `isomorphic-dompurify` | 2.20.0 | 3.21.0 | 🚫 deliberate pin, [documented above](#-isomorphic-dompurify-2200--3210--attempted-and-reverted) | +| `typescript` | 6.0.3 | 7.0.2 | ⬜ deferred to Phase 6 | +| **`prettier-plugin-tailwindcss`** | **0.5.14** | 0.8.1 | ✅ **taken to 0.7.4** — never tracked anywhere | +| `@types/google.maps` | 3.65.3 | 3.65.4 | ✅ done | +| `nodemailer` | 9.0.3 | 9.0.4 | ✅ done | + +The other 27 declared packages were already at latest. Two lessons in the shape of that table: a +plan that lists phases is not the same as a plan that lists *packages* — `prettier-plugin-tailwindcss` +sat three minors behind through eight phases because no phase's scope named it. And `npm outdated` +reported nuxt's latest as **4.4.8**, i.e. behind our installed 4.5.1; the `latest` dist-tag is 4.5.1, +so that column was a stale packument on npm's side, not a downgrade to chase. + +### Stopped at 0.7.4, because 0.8.0 corrupts import order in `.vue` files + +The obvious move was 0.8.1. It is wrong here. **`prettier-plugin-tailwindcss` 0.8.x silently +disables `@ianvs/prettier-plugin-sort-imports` on `.vue` files**, and the result is not "unsorted" — +it is *scrambled*, with blank lines inserted mid-block: + +```diff +-import { computed, ref, watch } from 'vue' ++import { computed, watch, ref } from 'vue' ++ +``` + +Measured by formatting the same tree with each version and diffing the results: + +| version | published | import order | class order | +| --- | --- | --- | --- | +| 0.5.14 | 2024-04-15 | ✅ correct | baseline | +| 0.6.14 | 2025-07-09 | ✅ correct | unchanged | +| **0.7.4** | 2026-04-27 13:55 | ✅ correct | 2 files, 3 lines | +| 0.8.0 | 2026-04-27 **13:57** | ❌ scrambled | — | +| 0.8.1 | 2026-07-15 | ❌ scrambled | — | + +Note the two minutes between 0.7.4 and 0.8.0: 0.7.4 is not a maintained branch, it is simply the +last release before the break. So this is a **hold, not a home** — see the re-attempt condition below. + +Upstream has it: [tailwindlabs/prettier-plugin-tailwindcss#465](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/465), +open since 2026-07-07. The `vue` parser lives under the plugin's `html` transform, whose `compatible` +list omits `@ianvs/prettier-plugin-sort-imports`; the `js` transform already has it, which is why +`.ts` files are unaffected and only `.vue` files scramble. `^0.7.4` resolves within 0.7.x, so the +manifest cannot drift into 0.8 on its own. + +**A first measurement of this was untrustworthy and got thrown away.** Comparing versions on a single +file copied to `/tmp` with `--no-config` let the plugin resolve a *different* Tailwind config than it +uses in-project, so its class-order output there described nothing real. The numbers above come from +formatting the actual tree in place with each version installed. The import-order finding survived +because plugin chaining does not depend on the Tailwind config; the class-order finding did not. + +### What 0.7.4 actually changes, and the bug it exposed + +Three lines in two files — and both are cosmetic in the browser, which is why **no source file is +touched in this PR**. `PrimaryPbButton.vue` loses a stray trailing space inside a class string, and +`TalkItem.vue` collapses a duplicated `grid grid`. + +That duplicate is the interesting part. `TalkItem.vue` puts HTML comments **inside** the `class` +attribute: + +```html +class=" + order-1 + lg:order-none +" +``` + +Attribute values are not parsed for comments, so the browser receives `` as class names. The duplicate `grid` the formatter removes comes +from the *word* "grid" in two of those comments. + +**Deliberately not fixed here**, and the reason is worth recording: the two affected `