83 lines
3.4 KiB
Go
83 lines
3.4 KiB
Go
// Package tw is kjol's native Go Tailwind v4 compiler.
|
|
//
|
|
// It lives on its own, not inside the bundler, because Tailwind is not a
|
|
// JavaScript concern. The bundler builds web/ (TSX, Solid, esbuild); the Tailwind
|
|
// engine only ever reads text and writes CSS, and the text it reads is just as
|
|
// likely to be Go — the gowasm kit authors its markup in Go and has no JS build at
|
|
// all. Burying the compiler in the bundler made every Go-only consumer drag a
|
|
// JavaScript bundler along for a CSS file.
|
|
//
|
|
// The scanner is language-agnostic: it pulls candidate class tokens out of any text
|
|
// file, so `class="flex gap-2"` in a .tsx and `vdom.Attr("class", "flex gap-2")` in
|
|
// a .go are found the same way.
|
|
//
|
|
// The engine itself is in tailwind.go — a from-scratch port of Tailwind v4's
|
|
// compiler. This file is the surface the rest of kjol calls.
|
|
package tw
|
|
|
|
import (
|
|
"github.com/tdewolff/minify/v2"
|
|
mincss "github.com/tdewolff/minify/v2/css"
|
|
)
|
|
|
|
var minifier *minify.M
|
|
|
|
func init() {
|
|
minifier = minify.New()
|
|
minifier.AddFunc("text/css", mincss.Minify)
|
|
}
|
|
|
|
// Scan extracts candidate utility class names from the files matched by patterns
|
|
// (globs, relative to baseDir). It reads text, not syntax: a candidate is any token
|
|
// that could plausibly be a class, and the compiler decides which ones actually are.
|
|
func Scan(baseDir string, patterns []string) []string {
|
|
return scanSources(baseDir, patterns)
|
|
}
|
|
|
|
// Compile compiles a Tailwind entry stylesheet against a set of candidates, and
|
|
// returns the CSS along with how many utilities it emitted (useful for a build log —
|
|
// a sudden drop usually means the scanner stopped seeing a source tree).
|
|
//
|
|
// entryCSS is the stylesheet source: typically `@import "tailwindcss";` plus an
|
|
// `@theme { … }` block. Anything else in it — @font-face, plain rules — passes
|
|
// through untouched.
|
|
func Compile(entryCSS, baseDir string, candidates []string) (css string, utilities int, err error) {
|
|
return twCompile(entryCSS, baseDir, candidates)
|
|
}
|
|
|
|
// CompileFiles is Scan + Compile + minify: the whole job, for a caller that just
|
|
// wants CSS out of a stylesheet and some source globs.
|
|
func CompileFiles(entryCSS, baseDir string, sourceGlobs []string) (string, error) {
|
|
compiled, _, err := twCompile(entryCSS, baseDir, scanSources(baseDir, sourceGlobs))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return Minify(compiled)
|
|
}
|
|
|
|
// CompileApp is Compile with kjol's shared extension layer prepended to entryCSS —
|
|
// the layering every kjol front-end (Solid and gowasm) shares:
|
|
//
|
|
// base Tailwind → kjol extensions (kjol_theme.css) → the app's brand entryCSS
|
|
//
|
|
// The kjol layer does the `@import "tailwindcss"` and defines the semantic tokens,
|
|
// the class-based dark variant and the chart palette, so entryCSS carries only
|
|
// brand. Compile itself stays a plain Tailwind engine (no kjol tokens) for callers
|
|
// that want exactly that.
|
|
func CompileApp(entryCSS, baseDir string, candidates []string) (css string, utilities int, err error) {
|
|
return twCompile(kjolThemeCSS+"\n"+entryCSS, baseDir, candidates)
|
|
}
|
|
|
|
// CompileAppFiles is Scan + CompileApp + minify: CompileFiles with the kjol
|
|
// extension layer prepended.
|
|
func CompileAppFiles(entryCSS, baseDir string, sourceGlobs []string) (string, error) {
|
|
compiled, _, err := CompileApp(entryCSS, baseDir, scanSources(baseDir, sourceGlobs))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return Minify(compiled)
|
|
}
|
|
|
|
// Minify shrinks compiled CSS.
|
|
func Minify(css string) (string, error) { return minifier.String("text/css", css) }
|