From dddba930ac30cbda109cf95cd8fdf6f2c6917405 Mon Sep 17 00:00:00 2001 From: Mohith Gajjela <109003762+Mohith26@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:51:18 -0500 Subject: [PATCH 1/2] Fix "Missing visitor keys for 'undefined'" error with multiple Prettier copies The plugin resolves `prettier/plugins/html` from its own dependency tree to obtain the parsers it wraps, but it did not export a matching printer, so the AST was printed by the running Prettier's builtin HTML printer. When a dependency tree contains more than one copy of Prettier straddling the v3.7 boundary (where the HTML AST discriminator was renamed from `type` to `kind`), the printer receives nodes whose discriminator it cannot read and throws "Missing visitor keys for 'undefined'". Export the HTML printer from the same `prettier/plugins/html` module the parsers are loaded from - mirroring the existing `svelte-ast` printer export - so the parser and printer always agree on the AST shape. Fixes #469 --- CHANGELOG.md | 4 +++- src/index.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62920a2..442ecd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Fix "Missing visitor keys for 'undefined'" error when multiple copies of Prettier exist in a dependency tree ([#469](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/469)) ## [0.8.1] - 2026-07-15 diff --git a/src/index.ts b/src/index.ts index 57beef6..c769f48 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1065,6 +1065,15 @@ let html = defineTransform({ vue: { dynamicAttrs: [':class', 'v-bind:class'] }, }, + // Use the printer from the same `prettier/plugins/html` module the parsers + // come from. When multiple copies of Prettier exist in a dependency tree the + // AST produced by our copy may not match what the running Prettier's builtin + // printer expects (e.g. Prettier v3.7 renamed the HTML AST `type` field to + // `kind`) causing a "Missing visitor keys for 'undefined'" error. + printers: { + html: {}, + }, + transform: transformHtml, }) From 72a531cec23933095a25da009fb5421970be1014 Mon Sep 17 00:00:00 2001 From: Mohith Gajjela <109003762+Mohith26@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:51:18 -0500 Subject: [PATCH 2/2] Add regression test for duplicated-Prettier version mismatch (#469) Adds a fixture with its own `prettier@3.6.2` install. The test copies the built plugin next to it so the plugin resolves the fixture's Prettier copy while the repo's Prettier CLI formats the file, reproducing the mismatched setup from the issue. Without the fix this fails with "Missing visitor keys for 'undefined'". --- .gitignore | 1 + tests/fixtures.test.ts | 20 ++++++++++++++ tests/fixtures/prettier-mismatch/index.html | 1 + tests/fixtures/prettier-mismatch/output.html | 1 + .../prettier-mismatch/package-lock.json | 27 +++++++++++++++++++ tests/fixtures/prettier-mismatch/package.json | 5 ++++ 6 files changed, 55 insertions(+) create mode 100644 tests/fixtures/prettier-mismatch/index.html create mode 100644 tests/fixtures/prettier-mismatch/output.html create mode 100644 tests/fixtures/prettier-mismatch/package-lock.json create mode 100644 tests/fixtures/prettier-mismatch/package.json diff --git a/.gitignore b/.gitignore index bd07d4e..4bed9a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules /dist +plugin-dist diff --git a/tests/fixtures.test.ts b/tests/fixtures.test.ts index 5c8c9a6..401abdf 100644 --- a/tests/fixtures.test.ts +++ b/tests/fixtures.test.ts @@ -160,4 +160,24 @@ describe('fixtures', () => { expect(formatted.trim()).toEqual(expected.trim()) }) } + + // https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/469 + test.concurrent('multiple copies of Prettier in a dependency tree', async ({ expect }) => { + let fixturePath = path.resolve(__dirname, 'fixtures/prettier-mismatch') + let pluginCopyPath = path.resolve(fixturePath, 'plugin-dist') + + // Copy the built plugin into the fixture so it resolves the fixture's + // copy of Prettier while the CLI runs the repo's copy + await fs.cp(path.resolve(__dirname, '../dist'), pluginCopyPath, { recursive: true }) + + let inputPath = path.resolve(fixturePath, 'index.html') + let outputPath = path.resolve(fixturePath, 'output.html') + let cmd = `${binPath} ${inputPath} --plugin ${pluginCopyPath}/index.mjs` + + let results = await execAsync(cmd) + let formatted = results.stdout.replace(/\r\n/g, '\n') + let expected = await fs.readFile(outputPath, 'utf-8').then((c) => c.replace(/\r\n/g, '\n')) + + expect(formatted.trim()).toEqual(expected.trim()) + }) }) diff --git a/tests/fixtures/prettier-mismatch/index.html b/tests/fixtures/prettier-mismatch/index.html new file mode 100644 index 0000000..7593a7e --- /dev/null +++ b/tests/fixtures/prettier-mismatch/index.html @@ -0,0 +1 @@ +
diff --git a/tests/fixtures/prettier-mismatch/output.html b/tests/fixtures/prettier-mismatch/output.html new file mode 100644 index 0000000..04a62c8 --- /dev/null +++ b/tests/fixtures/prettier-mismatch/output.html @@ -0,0 +1 @@ +
diff --git a/tests/fixtures/prettier-mismatch/package-lock.json b/tests/fixtures/prettier-mismatch/package-lock.json new file mode 100644 index 0000000..8528cb8 --- /dev/null +++ b/tests/fixtures/prettier-mismatch/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "prettier-mismatch", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "prettier": "3.6.2" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + } +} diff --git a/tests/fixtures/prettier-mismatch/package.json b/tests/fixtures/prettier-mismatch/package.json new file mode 100644 index 0000000..76e5035 --- /dev/null +++ b/tests/fixtures/prettier-mismatch/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "prettier": "3.6.2" + } +}