Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/interop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ name: Interop
# Early warning, not a gate: checks the pages that name other plugins against
# them (bench/interop.mjs) — /shadcn's table and combined config against
# @shadcn/lint, /migration/from-better-tailwindcss's rule map against
# eslint-plugin-better-tailwindcss — with the versions the pages name (pinned in
# bench/package-lock.json) and with their latest releases. A red run means a
# eslint-plugin-better-tailwindcss, /migration/from-eslint-plugin-tailwindcss's
# against eslint-plugin-tailwindcss — with the versions the pages name (pinned
# in bench/package-lock.json) and with their latest releases. A red run means a
# page needs updating, or that one of them changed what it reports.
on:
schedule:
Expand Down Expand Up @@ -46,7 +47,7 @@ jobs:
working-directory: bench
run:
npm install --no-save @shadcn/lint@latest eslint-plugin-better-tailwindcss@latest
vite-plus@latest
eslint-plugin-tailwindcss@latest vite-plus@latest

- name: Check the pages against them
working-directory: bench
Expand Down
10 changes: 7 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -904,9 +904,13 @@ breaks one fails `pnpm test` (or CI) instead of shipping:
- **What the examples do** — `doc-examples.test.ts` runs every ✗ / ✓ example of every rule page
(EN/ES) through the real oxlint; `doc-configs.test.ts` runs every `.oxlintrc.json` snippet;
`readme-hero.test.ts` the root README's hero; `skill.test.ts` the agent skill.
- **What other tools do** — `shadcn-interop.test.ts` and `better-tailwindcss-map.test.ts` here, and
`bench/interop.mjs` weekly (`interop.yml`) against @shadcn/lint and better-tailwindcss, pinned and
latest; `bench/vite-plus.mjs` in the same job holds `/monorepo`'s Vite+ warning against `vp lint`.
- **What other tools do** — `shadcn-interop.test.ts`, `better-tailwindcss-map.test.ts` and
`eslint-plugin-tailwindcss-map.test.ts` here, and `bench/interop.mjs` weekly (`interop.yml`)
against @shadcn/lint, better-tailwindcss and eslint-plugin-tailwindcss, pinned and latest;
`bench/vite-plus.mjs` in the same job holds `/monorepo`'s Vite+ warning against `vp lint`.
eslint-plugin-tailwindcss registers as `tailwindcss` too (same rule prefix, same
`settings.tailwindcss` key, other keys), so oxlint refuses to load it beside this plugin:
`interop.mjs` lints it alone, with an absolute `cssConfigPath`.
- **What the benchmark measured** — `/benchmark` and `/comparison` are generated from
`bench/results/latest.json`; `bench.yml` checks weekly that a fresh `score.mjs` run agrees.
- **What the site serves** — `packages/docs/scripts/check-dist.ts` (in the docs build: titles,
Expand Down
5 changes: 4 additions & 1 deletion bench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ main test suite (`tests/docs/shadcn-interop.test.ts`).

For `/migration/from-better-tailwindcss`: every row of `packages/docs/data/better-tailwindcss.json`
— its example must be reported by their rule and by ours, each with the row's options — and every
rule of eslint-plugin-better-tailwindcss must have a row.
rule of eslint-plugin-better-tailwindcss must have a row. The same for
`/migration/from-eslint-plugin-tailwindcss` and `packages/docs/data/eslint-plugin-tailwindcss.json`,
with eslint-plugin-tailwindcss run as an oxlint JS plugin on its own: it registers as `tailwindcss`
too, so oxlint won't load it beside this plugin.

Exits 1 on any disagreement. `.github/workflows/interop.yml` runs it weekly with the pinned versions
and with the latest releases.
Expand Down
41 changes: 39 additions & 2 deletions bench/interop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
// packages/docs/data/better-tailwindcss.json — its example must be reported
// by their rule and by ours, each with the row's options — and every rule of
// eslint-plugin-better-tailwindcss must have a row.
// - /migration/from-eslint-plugin-tailwindcss: the same for
// packages/docs/data/eslint-plugin-tailwindcss.json, against
// eslint-plugin-tailwindcss run as an oxlint JS plugin. It registers as
// `tailwindcss` too, so it is linted on its own, never beside this plugin.
//
// Exits 1 on any disagreement.
//
Expand Down Expand Up @@ -51,6 +55,9 @@ const data = JSON.parse(readFileSync(join(REPO, 'packages/docs/data/shadcn-lint.
const btw = JSON.parse(
readFileSync(join(REPO, 'packages/docs/data/better-tailwindcss.json'), 'utf8'),
)
const eptw = JSON.parse(
readFileSync(join(REPO, 'packages/docs/data/eslint-plugin-tailwindcss.json'), 'utf8'),
)
const page = readFileSync(join(REPO, 'packages/docs/shadcn.md'), 'utf8')
const version = (pkg) =>
JSON.parse(readFileSync(join(BENCH, 'node_modules', pkg, 'package.json'), 'utf8')).version
Expand All @@ -74,6 +81,9 @@ for (const { file, example } of exampleFiles(data.concerns)) {
btw.rules.forEach((row, i) =>
writeFileSync(join(project, 'src', `btw-${i}.tsx`), exampleModule(row.example)),
)
eptw.rules.forEach((row, i) =>
writeFileSync(join(project, 'src', `eptw-${i}.tsx`), exampleModule(row.example)),
)

function lint(config) {
writeFileSync(join(project, '.oxlintrc.json'), JSON.stringify(config))
Expand Down Expand Up @@ -140,14 +150,41 @@ const mapping = mappingGaps(
ours,
)

// 4. eslint-plugin-tailwindcss: each row's two rules, with default options. A
// relative cssConfigPath resolves against the nearest eslint.config.* or
// package.json above the linted file — bench/'s here — so it gets an absolute one.
const eptwTheirs = lint({
categories: { correctness: 'off' },
jsPlugins: ['eslint-plugin-tailwindcss'],
settings: { tailwindcss: { cssConfigPath: join(project, 'app/globals.css') } },
rules: Object.fromEntries(eptw.rules.map((r) => [`tailwindcss/${r.theirs}`, 'warn'])),
})
const eptwOurs = lint({
categories: { correctness: 'off' },
jsPlugins: [values.plugin === 'local' ? LOCAL_DIST.replaceAll('\\', '/') : 'oxlint-tailwindcss'],
settings: { tailwindcss: { entryPoint: 'app/globals.css' } },
rules: Object.fromEntries(eptw.rules.map((r) => [`tailwindcss/${r.ours}`, 'warn'])),
})
const eptwPlugin = await import('eslint-plugin-tailwindcss')
const eptwMapping = mappingGaps(
eptw.rules,
Object.keys((eptwPlugin.default ?? eptwPlugin).rules),
eptwTheirs,
eptwOurs,
{ file: 'eptw', plugin: 'tailwindcss' },
)

console.log(
`@shadcn/lint ${version('@shadcn/lint')} (the page says ${data.shadcnLint}), ` +
`eslint-plugin-better-tailwindcss ${version('eslint-plugin-better-tailwindcss')} ` +
`(the page says ${btw.betterTailwindcss}), oxlint ${version('oxlint')}, ` +
`(the page says ${btw.betterTailwindcss}), ` +
`eslint-plugin-tailwindcss ${version('eslint-plugin-tailwindcss')} ` +
`(the page says ${eptw.eslintPluginTailwindcss}), oxlint ${version('oxlint')}, ` +
`oxlint-tailwindcss ${values.plugin}`,
)
const problems = [...column, ...gaps, ...mapping]
const problems = [...column, ...gaps, ...mapping, ...eptwMapping]
for (const line of problems) console.log(` ✗ ${line}`)
if (problems.length > 0) process.exit(1)
console.log(` ✓ /shadcn: ${data.concerns.length} concerns, the table and the combined config hold`)
console.log(` ✓ /migration/from-better-tailwindcss: ${btw.rules.length} rules map`)
console.log(` ✓ /migration/from-eslint-plugin-tailwindcss: ${eptw.rules.length} rules map`)
18 changes: 13 additions & 5 deletions bench/lib/interop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,22 @@ export function generatedFence(markdown, id) {
}

/**
* /migration/from-better-tailwindcss: a row whose example one of its two
* rules doesn't report, and a rule of theirs with no row.
* A migration guide (/migration/from-better-tailwindcss,
* /migration/from-eslint-plugin-tailwindcss): a row whose example one of its
* two rules doesn't report, and a rule of theirs with no row. Row `i`'s example
* is linted in `<file>-<i>.tsx`; their diagnostics carry `<plugin>(<rule>)`.
*/
export function mappingGaps(rows, theirRules, firedTheirs, firedOurs) {
export function mappingGaps(
rows,
theirRules,
firedTheirs,
firedOurs,
{ file: prefix = 'btw', plugin = 'better-tailwindcss' } = {},
) {
const out = []
rows.forEach((row, i) => {
const file = `btw-${i}.tsx`
if (!firedTheirs.get(file)?.has(`better-tailwindcss/${row.theirs}`)) {
const file = `${prefix}-${i}.tsx`
if (!firedTheirs.get(file)?.has(`${plugin}/${row.theirs}`)) {
out.push(`${row.theirs}: doesn't report ${row.example}`)
}
if (!firedOurs.get(file)?.has(`tailwindcss/${row.ours}`)) {
Expand Down
Loading
Loading