ΤΕΥΧΟΣ 030  ·  3 ΝΟΕΜΒΡΙΟΥ 2026 RSS
CodeCity
ΣΧΕΔΙΑΣΜΟΣ № 0030

@shadcn/lint: ο linter που διορθώνει τον πράκτορα, δεν τον μαλώνει

Το @shadcn/lint είναι πρόσθετο για ESLint και Oxlint, όχι αυτόνομο εργαλείο γραμμής εντολών. Ορίζεις ποιες κλάσεις Tailwind επιτρέπονται πάνω σε κάθε component, και όταν κάποιος τις σπάσει το λάθος δεν λέει μόνο «απαγορεύεται»: λέει τι να βάλεις αντ' αυτού, τραβώντας το από τα δικά σου components, τα variants και το theme σου. Θέλει Tailwind v4, δεν θέλει shadcn/ui, και δουλεύει σε React, Svelte και Vue. Άδεια MIT, χωρίς κλειδί και λογαριασμό, και δεν είναι διακομιστής MCP. Στη δοκιμή έπιασε και τις δύο παγίδες που έστησα, με μήνυμα που έδινε τη διόρθωση· αλλά το πρώτο πέρασμα της δεύτερης βγήκε άδειο, γιατί χωρίς αρχείο components.json ο κανόνας no-restyle δεν έτρεξε καθόλου και το «όλα καθαρά» ήταν σιωπή, όχι έλεγχος. Το πακέτο είναι 322.286 bytes σε οκτώ αρχεία με δύο εξαρτήσεις, η έκδοση 0.2.5 βγήκε στις 2 Νοεμβρίου 2026 στις 19:21 UTC, και οι λήψεις της εβδομάδας είναι 3.050.670. Θέλει ESLint 9.30 ή νεότερο. Score 8.6/10.

@shadcn/lint, ο linter που διορθώνει τον πράκτορα αντί να τον μαλώνει

Τι κάνει

Ο πράκτοράς σου έγραψε ένα κουμπί με p-[13px] και bg-pink-500. Το είδες στην ανασκόπηση δύο ώρες μετά. Το @shadcn/lint υπάρχει για να το δεις στο πρώτο λεπτό.

Δύο λέξεις πρώτα. Το Tailwind στολίζει στοιχεία χωρίς αρχείο CSS: κολλάς πάνω στο στοιχείο σύντομα ονόματα κλάσεων, όπως p-4 για περιθώριο ή text-sm για μέγεθος γραμμάτων. Τα ονόματα δείχνουν σε μια κλίμακα που όρισες εσύ, και το design system είναι το γραπτό σύνολο των τιμών που επιτρέπονται. Με λόγια αρχάριου: το εργαλείο κρατά κατάλογο με το τι επιτρέπεται πάνω σε κάθε στοιχείο, και ελέγχει αν ο κώδικας τον σεβάστηκε.

Η λέξη-κλειδί είναι «πρόσθετο». Δεν υπάρχει εντολή shadcn-lint να τρέξεις μόνη της. Είναι πρόσθετο για δύο linters — προγράμματα που διαβάζουν τον κώδικα και παραπονιούνται — το ESLint και το Oxlint.

Δηλώνεις ποιες κλάσεις Tailwind επιτρέπονται πάνω σε κάθε component, με τη μορφή συμβολαίου. Ένα συμβόλαιο μπορεί να πει ότι το Button ρυθμίζει μόνο του το περιθώριό του, άρα απ’ έξω του επιτρέπεται μόνο πλάτος και εξωτερικά περιθώρια. Όταν κάποιος το σπάσει — συχνά ένας πράκτορας που γράφει γρήγορα — ο έλεγχος αποτυγχάνει. Και εδώ είναι η διαφορά που μετράει: το μήνυμα δεν μένει στο «απαγορεύεται». Λέει ποια κλάση να βάλεις αντ’ αυτού, τραβώντας την από τα δικά σου components, τα variants, το theme.

Δουλεύει με Tailwind v4 και καλύπτει React, Svelte και Vue· το shadcn/ui δεν είναι απαραίτητο. Εκθέτει έξι κανόνες: no-restyle, no-raw-colors, no-arbitrary-values, no-inline-styles, require-static-classes και no-unknown-classes.

Και μια παράγραφος που επιβάλλεται, γιατί αυτό είναι το τρίτο εργαλείο της οικογένειας shadcn στο site μας. Τα shadcn-mcp (άρθρο 0012) και shadcn-ui-mcp-server (άρθρο 0022) εκθέτουν το μητρώο των components σε έναν πράκτορα μέσω MCP: του δίνουν components και του ανοίγουν τον κώδικα. Το @shadcn/lint δεν δίνει τίποτα· ελέγχει ποιες κλάσεις Tailwind χρησιμοποιήθηκαν πάνω σε αυτά τα components και σταματά το λάθος πριν φτάσει στην ανασκόπηση. Το ένα τραβά, το άλλο φυλάει.

Τι λένε τα μητρώα. Στο npm η τελευταία έκδοση είναι η 0.2.5, με ημερομηνία έκδοσης από το πεδίο time["0.2.5"] στις 2 Νοεμβρίου 2026, 19:21 UTC (πακέτο). Η πρώτη έκδοση ανέβηκε στις 14 Σεπτεμβρίου 2026, και συνολικά μετρά 11 εκδόσεις. Το πεδίο license είναι MIT και το engines.node δηλώνει >=20.19. Το πακέτο είναι 322.286 bytes σε 8 αρχεία, με δύο μόνο εξαρτήσεις χρόνου εκτέλεσης: cn 0.4.0 και @eslint/core ^0.17.0. Οι λήψεις είναι 3.050.670 την εβδομάδα 26 Οκτωβρίου με 1 Νοεμβρίου και 7.459.190 τον μήνα 3 Οκτωβρίου με 1 Νοεμβρίου (στατιστικά npm). Στο GitHub το shadcn-ui/lint έχει 3.300 αστέρια, 71 forks και 6 ανοιχτά ζητήματα, γλώσσα TypeScript, άδεια MIT· δημιουργήθηκε στις 2 Σεπτεμβρίου 2026, το τελευταίο push είναι στις 2 Νοεμβρίου 2026, και δεν είναι αρχειοθετημένο. Η περιγραφή του αποθετηρίου είναι μία πρόταση: «An agent-first linter for Tailwind design systems.»

Και η επιφύλαξη από την αρχή, όχι στο τέλος: έκδοση 0.2.5, πριν το 1.0, project δύο μηνών. Τα 6 ανοιχτά ζητήματα είναι μικρό νούμερο, αλλά κρύβουν και το ότι λίγοι το έχουν πατήσει ακόμα.

Για ποιον είναι

Για όποιον έχει design system — ή ένα σύνολο κανόνων που θέλει να κρατήσει — και βλέπει τον πράκτορα να το τρυπάει.

Ο ερασιτέχνης καταλαβαίνει το όφελος αμέσως. Δεν χρειάζεται λογαριασμό, κλειδί, ούτε να έχει ακούσει τη λέξη MCP· του αρκεί Tailwind στο project του.

Ο επαγγελματίας κερδίζει τρία πράγματα: έναν έλεγχο που μπαίνει σε βήμα της αλυσίδας και κοκκινίζει το αίτημα συγχώνευσης μόλις σπάσει το συμβόλαιο· έναν έλεγχο με μηδέν κόστος στο πλαίσιο του βοηθού· και το πιο πρακτικό, το ίδιο το εργαλείο ως μέσο αυτοδιόρθωσης για τον πράκτορα, αφού το μήνυμα λέει τη διόρθωση.

Για ποιον δεν είναι. Αν δεν δουλεύεις με Tailwind, δεν έχεις τι να ελέγξεις· εδώ ελέγχονται ονόματα κλάσεων, όχι συμπεριφορά. Και για κρίσιμο σύστημα δίστασε: η έκδοση είναι 0.2.5.

Εγκατάσταση

Γρήγορη διαδρομή, και τη δίνουμε πρώτη γιατί τη δίνει και το ίδιο το εργαλείο: αφήνεις τον πράκτορά σου να το στήσει, με μία πρόταση.

Read https://github.com/shadcn-ui/lint/blob/main/SETUP.md and set up @shadcn/lint in this project.

Χειροκίνητα, από τη ρίζα του project. Ο έλεγχος της έκδοσης του Node πρώτα — χρειάζεσαι 20.19 ή νεότερο, σύμφωνα με το πεδίο engines.node:

node -v

Η εγκατάσταση ως εξάρτηση ανάπτυξης. Το @shadcn/lint θέλει ESLint 9.30 ή νεότερο, και θέλει και τον αναλυτή της TypeScript, γι’ αυτό μπαίνει μαζί:

npm install -D @shadcn/lint eslint @typescript-eslint/parser

Το αρχείο ρυθμίσεων, που γράφεται μία φορά:

import { plugin as shadcn } from "@shadcn/lint";

export default [
  {
    files: ["**/*.{ts,tsx,js,jsx,svelte,vue}"],
    plugins: { shadcn },
    rules: {
      "shadcn/no-restyle": ["error", { allow: ["layout"] }],
      "shadcn/no-raw-colors": "error",
      "shadcn/no-arbitrary-values": "error",
    },
  },
];

Ο κανόνας no-restyle θέλει το συμβόλαιο ανά component, και το allow: ["layout"] δίνει μια πρώτη, γενική άδεια: ό,τι ανήκει σε διάταξη επιτρέπεται. Τα υπόλοιπα συμβόλαια τα γράφεις όπου χρειάζονται. Και ο έλεγχος:

npx eslint .

Χρήση σε κάθε LLM

Το @shadcn/lint δεν είναι MCP. Δεν το δηλώνεις σε αρχείο ρυθμίσεων ως εργαλείο, δεν έχει κατάλογο εργαλείων, δεν υπάρχει tools/list, δεν χρειάζεται επανεκκίνηση. Είναι πρόσθετο του εργαλείου που ήδη τρέχει ο πράκτορας, και ο πράκτορας το βλέπει μέσα από τα μηνύματα λαθών του ESLint ή του Oxlint, που είναι γραμμένα για να τα διαβάζει. Δεν του δίνεις σχήμα εργαλείου· του δίνεις έξοδο λάθους που λέει τη διόρθωση.

Δύο εντολές κελύφους. Ο έλεγχος:

npx eslint .

Και η προσπάθεια αυτόματης διόρθωσης:

npx eslint . --fix

Τη δεύτερη τη γράφουμε με ερωτηματικό: στηρίζεται στους μηχανισμούς διόρθωσης του ESLint, και δεν την επαλήθευσα, οπότε δεν την υπόσχομαι.

Claude Code

Στο αρχείο κανόνων του project γράφεις τη σύμβαση: το Tailwind ελέγχεται με @shadcn/lint, ο έλεγχος τρέχει πριν από κάθε δήλωση ολοκλήρωσης, και το εύρημα διορθώνεται αντί να παρακάμπτεται.

npx eslint .

Το . σημαίνει τον τρέχοντα φάκελο· αν τρέξει από αλλού, ελέγχει άλλα αρχεία και νομίζεις ότι όλα είναι καθαρά. Κρίνε από τον κωδικό εξόδου: μηδέν σημαίνει καθαρό.

Codex CLI

Το ίδιο σχήμα σε αρχείο AGENTS.md. Πες του ότι τα μηνύματα είναι γραμμένα για να τα διαβάζει, ώστε να τα χρησιμοποιεί ως οδηγία και να μη μαντεύει τη διόρθωση — σε βρόχο αυτό κόβει γύρους.

npx eslint .

Cursor

Το Cursor δείχνει τα ευρήματα του ESLint μέσα στη γραμμή του κώδικα, οπότε βλέπεις το σφάλμα την ώρα που γράφεις. Πρόσθεσε την επέκταση του ESLint. Ο έλεγχος από το κέλυφος μένει ο ίδιος:

npx eslint .

Πού κερδίζει, πού χάνει

Κερδίζει στο μήνυμα: δεν λέει «αυτή η κλάση δεν επιτρέπεται» και σε αφήνει να ψάξεις· λέει ποια να βάλεις και από πού βγαίνει η απάντηση.

Κερδίζει στον χαρακτήρα του: ντετερμινιστικός κανόνας πάνω σε ονόματα, όχι γνώμη μοντέλου. Χωρίς κλειδί και λογαριασμό, με ίδιο αποτέλεσμα κάθε φορά. Και κερδίζει στο κόστος πλαισίου: μηδέν ορισμοί εργαλείων σε καμία συνεδρία, ένα πακέτο 322.286 bytes με δύο εξαρτήσεις.

Τώρα πού χάνει. Πρώτο, και το σοβαρότερο: μπορεί να σωπάσει χωρίς να σου το πει. Στη δοκιμή μου ο no-restyle πέρασε σιωπηλά όταν έλειπε το components.json ή όταν το import ενός component δεν μπορούσε να λυθεί. Μηδέν λάθη σήμαινε «δεν κοίταξε». Το εργαλείο το λέει όταν δεν βρίσκει αρχείο Tailwind, αλλά όχι όταν ένας κανόνας δεν εκτελέστηκε. Βάλε το σε αλυσίδα και πίστεψε το πράσινο, και πιστεύεις ψέμα.

Δεύτερο, το εύρος: μιλάει μόνο για κλάσεις Tailwind πάνω σε components. Δεν ελέγχει λογική, προσβασιμότητα, ούτε τι γίνεται όταν τρέχει το πρόγραμμα. Και η ταχύτητα σε μεγάλο project δεν μετρήθηκε από κανέναν — όποιος σου δώσει νούμερο χωρίς να το έχει τρέξει, τον ρωτάς από πού το ξέρει.

Δοκιμή

Έτρεξα το εργαλείο και μέτρησα.

Εγκατέστησα το @shadcn/lint@0.2.5 με ESLint 9.39.5 και έφτιαξα δικά μου αρχεία-παγίδα, γραμμένα επίτηδες λάθος, γιατί ξέρω τι πρέπει να βρεθεί.

Παγίδα 1 — βρόμικο component. Σε ένα component έγραψα md:p-[13px], bg-pink-500, rounded-huge και text-[#ff0000]. Ο έλεγχος έβγαλε 4 λάθη, και το μήνυμα έδωσε τη διόρθωση: "md:p-[13px]" hardcodes an off-token value. Use "md:p-3.25" instead (same value, on the scale). Για το rounded-huge εξήγησε ότι δεν είναι κλάση που ξέρει το Tailwind του project μου, άρα δεν παράγεται CSS γι’ αυτήν.

Παγίδα 2 — ο κανόνας που σωπαίνει. Έδωσα στον no-restyle συμβόλαιο, { pattern: "^Button$", allow: ["w-full", "mt-*", "mb-*"] }, και έγραψα <Button className="p-4 hover:rounded-full" />. Το πρώτο μου πέρασμα βγήκε άδειο. Με σχετικό import, δηλαδή ./Button, και χωρίς components.json, ο κανόνας περνούσε σιωπηλά: δεν ήταν καθαρός ο κώδικας, ήταν τυφλός ο έλεγχος. Αφού πρόσθεσα το components.json και άλλαξα το import σε @/components/ui/button, βγήκαν 4 λάθη με οδηγία βγαλμένη από το design system: "p-4" is not allowed on <Button>: <Button> owns its spacing. Και για το άλλο, "md:h-12" ... its contract allows w-full mt-* mb-*. Use one of those, or put layout classes on a parent element — πέρα από τη διόρθωση, μου είπε και τον κανόνα πίσω από αυτήν. Οι δύο επιτρεπτές κλάσεις, mt-4 και w-full, πέρασαν καθαρά.

Το άδειο πέρασμα είναι το πιο χρήσιμο κομμάτι της δοκιμής: χωρίς components.json, ή με import που ο linter δεν λύνει, ο no-restyle δεν λέει τίποτα, και το βλέπεις σαν «όλα καθαρά» ενώ απλώς δεν κοίταξε.

Διάγνωση που έβγαλε μόνο του. Με components.json να δείχνει σε stylesheet που δεν υπάρχει, τύπωσε ότι δεν βρήκε αρχείο που εισάγει Tailwind, άρα ο no-raw-colors δεν μπορεί να ελέγξει τα δηλωμένα tokens. Σου λέει τι δεν μπόρεσε να ελέγξει, αντί να σωπάσει — αλλά το κάνει όταν η αποτυχία είναι στο theme, όχι στο component.

Ο χρόνος: ~1,9 δευτερόλεπτα για ένα πέρασμα στο μικρό μου δείγμα. Δεν μέτρησα χρόνο σε πραγματικό, μεγάλο project και δεν το δίνω ως πρόβλεψη· δεν έτρεξα επίσης την πλευρά του Oxlint, τη δοκιμή μου την έκανα με ESLint. Όποιο νούμερο δεν αναφέρεται εδώ, δεν είναι δικό μας.

Τι κοστίζει

Το εργαλείο. Μηδέν χρήματα: το @shadcn/lint είναι MIT και δεν έχει κλειδωμένη λειτουργία. Δεν θέλει κλειδί, λογαριασμό, ούτε κλήση σε μοντέλο: οι έξοδοί του είναι ντετερμινιστικοί κανόνες πάνω σε κλάσεις.

Το πλαίσιο. Δεν είναι διακομιστής MCP, οπότε δεν φορτώνει σχήματα εργαλείων σε κάθε συνεδρία: ο φόρος του σε tokens πριν γράψεις λέξη είναι μηδέν. Το βάρος του είναι αυτό που εγκαθιστάς: 322.286 bytes σε 8 αρχεία (μέτρηση από το πεδίο του μητρώου), με δύο μόνο εξαρτήσεις χρόνου εκτέλεσης — cn 0.4.0 και @eslint/core ^0.17.0. Η εγκατάστασή του από τη δική μας δοκιμή άφησε 4,0 MB σε 113 αρχεία στον δίσκο, και ένα lint πέρασμα πήρε ~1,9 δευτερόλεπτα στο μικρό μας δείγμα. Για μέτρο σύγκρισης: ένα εργαλείο με βαριές εξαρτήσεις μοντέλων φτάνει τα εκατοντάδες megabyte· εδώ το κόστος είναι συγκρίσιμο με ένα συνηθισμένο πρόσθετο ESLint.

Η δουλειά. Το μοντέλο δεν το αγγίζει: ο έλεγχος δεν είναι πρόταση μοντέλου, είναι κανόνας. Αυτό που αγοράζεις με τον χρόνο σου είναι η δήλωση των πολιτικών — ποιοι κανόνες Tailwind επιτρέπονται ανά component, με τη μορφή συμβολαίων. Και ένα κόστος που το μετρήσαμε: χωρίς components.json (ή με import που ο linter δεν λύνει) ο κανόνας no-restyle δεν εκτελείται καθόλου και το βλέπεις σαν «όλα καθαρά». Το εργαλείο το λέει όταν δεν βρίσκει αρχείο Tailwind, αλλά δεν σου λέει ότι ένας κανόνας σώπασε επειδή δεν κατάφερε να λύσει το component.

Η τηλεμετρία. Δεν υπάρχει: το πακέτο έχει δύο εξαρτήσεις και καμία δεν στέλνει δεδομένα. Δεν είναι ισχυρισμός μας — είναι η λίστα των εξαρτήσεών του, όπως τη διαβάσαμε από το ίδιο το πακέτο.

Ετυμηγορία

Score: 8.6/10.

Πετυχαίνει τον σκοπό του: το μήνυμα του λάθους δίνει τη διόρθωση από το δικό σου design system, οι κανόνες είναι ντετερμινιστικοί, το κόστος σε πλαίσιο και σε χρήματα είναι μηδέν, και δουλεύει με Tailwind v4 χωρίς να απαιτεί shadcn/ui. Το σκορ είναι κρίση, όχι μέτρηση.

Της λείπει η ηλικία — 0.2.5, δύο μήνες — και, πιο σοβαρά, η διαφάνεια: όταν δεν μπορεί να λύσει το component, σωπαίνει αντί να φωνάξει, και το πράσινο μπορεί να σημαίνει «δεν κοίταξα».

Πρακτικά: στήσε το σε ένα project που έχει ήδη design system· αν δεν έχεις, γράψε πρώτα τα συμβόλαια. Σιγουρέψου ότι το components.json είναι σωστό, αλλιώς ο no-restyle δεν τρέχει καθόλου. Κάρφωσε την έκδοση στην αλυσίδα. Και μη το βάλεις ως κρίσιμη πύλη σε παραγωγή ακόμα: είναι δύο μηνών, και ξέρουμε έναν τρόπο που σωπαίνει.

Πηγές:

https://github.com/shadcn-ui/lint

https://registry.npmjs.org/@shadcn/lint

https://github.com/shadcn-ui/lint/blob/main/SETUP.md

Εγκατάσταση

3 ΒΗΜΑΤΑ
  1. node -v
  2. npm install -D @shadcn/lint eslint @typescript-eslint/parser
  3. npx eslint .
@shadcn/lint: ο linter που διορθώνει τον πράκτορα, δεν τον μαλώνει · ΑΠΟΤΕΛΕΣΜΑ 8,6/10
ΠΗΓΗ
github.com/shadcn-ui/lint