Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynamic (Jinja-generated) classes #8

Merged
merged 4 commits into from
Mar 8, 2024
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ A Lektor plugin that adds Tailwind CSS to your project seamlessly.

```javascript
module.exports = {
content: ['./templates/**/*.{html,j2}'],
// './' refers to the lektor build output directory, NOT the project dir
content: ['./**/*.html'],
theme: {
extend: {},
},
Expand Down
83 changes: 21 additions & 62 deletions lektor_tailwind.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
from lektor.pluginsystem import Plugin
from pytailwindcss import get_bin_path, install

__version__ = "0.1.2"
GRACEFUL_TIMEOUT = 5

__version__ = "0.1.3"

class TailwindPlugin(Plugin):
name = "lektor-tailwind"
Expand All @@ -22,86 +20,47 @@ def __init__(self, env, id):
self.css_path = config.get("css_path", "static/style.css")
self.input_css = os.path.join(self.env.root_path, "assets", self.css_path)
self.tailwind: subprocess.Popen | None = None
self.config_file = "tailwind.config.js"

def on_setup_env(self, **extra):
self.init_tailwindcss()

def init_tailwindcss(self):
if not os.path.exists(self.tailwind_bin):
install(bin_path=self.tailwind_bin)
filename = "tailwind.config.js"
if not os.path.exists(os.path.join(self.env.root_path, filename)):
if not os.path.exists(os.path.join(self.env.root_path, self.config_file)):
subprocess.run(
[self.tailwind_bin, "init"], check=True, cwd=self.env.root_path
)

def _run_watcher(self, output_path: str):
if not self.input_exists():
return

cmd = [
self.tailwind_bin,
"--input",
self.input_css,
"--output",
os.path.join(output_path, self.css_path),
"--watch",
]
if os.environ.get("NODE_ENV") == "production":
cmd.append("--minify")

self.tailwind = subprocess.Popen(cmd, cwd=self.env.root_path)
def _get_tailwind_args(self, output_path, *extra_args):
return [self.tailwind_bin,
"-c",
os.path.join(self.env.root_path, self.config_file),
"-i",
self.input_css,
"-o",
os.path.join(output_path, self.css_path),
*extra_args,]

def input_exists(self) -> bool:
return os.path.exists(self.input_css)

def should_minify(self) -> bool:
return not self.watch or os.environ.get("NODE_ENV") == "production"

def compile_css(self, output_path: str):
minify = ["--minify"] if self.should_minify() else []
subprocess.run(
[
self.tailwind_bin,
"-i",
self.input_css,
"-o",
os.path.join(output_path, self.css_path),
"--minify",
],
self._get_tailwind_args(output_path, *minify),
check=True,
cwd=self.env.root_path,
cwd=output_path,
)

def on_server_spawn(self, **extra):
self.watch = True

def on_server_stop(self, **extra):
self.watch = False
if self.tailwind is not None:
self.tailwind.terminate()
try:
self.tailwind.communicate(GRACEFUL_TIMEOUT)
except subprocess.TimeoutExpired:
self.tailwind.kill()
self.tailwind = None

def on_before_build_all(self, builder, **extra):
if not self.input_exists() or self.tailwind is not None or not self.watch:
return
self._run_watcher(builder.destination_path)

def on_before_build(self, builder, source, prog, **extra):
if source.source_filename != self.input_css:
def on_after_build_all(self, builder, **extra):
if not self.input_exists():
return

# The input stylesheet is being built. We don't want to let
# Lektor "build" it (i.e. copy it to the output directory),
# since that will potentially overwrite any Tailwind-compiled
# output that is already there.

# Here we monkey-patch Lektor's build program to disable it
prog.build_artifact = lambda artifact: None

# Instead, we run tailwind to compile the self.input_css to
# the output directory. (We skip this if we're already
# running tailwind in --watch mode, since, in that case, it
# will rebuild the CSS on it's own.)
if self.tailwind is None:
self.compile_css(builder.destination_path)
self.compile_css(builder.destination_path)
2 changes: 1 addition & 1 deletion tests/example/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const colors = require("tailwindcss/colors");

module.exports = {
content: ["./templates/**/*.html"],
content: ["./**/*.html"],
theme: {
extend: {
colors: {
Expand Down
8 changes: 4 additions & 4 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ class LektorServerFixture:
def __init__(self, port=9527):
proc = subprocess.Popen(
("lektor", "server", "-p", f"{port:d}"),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=0,
Expand All @@ -101,9 +100,6 @@ def _close():
try:
self.sel.close()
proc.terminate()
proc.stdin.close()
proc.stdout.close()
proc.stderr.close()
proc.wait(5)
except (subprocess.TimeoutExpired, OSError):
proc.kill()
Expand Down Expand Up @@ -186,6 +182,8 @@ def test_server_rebuilds_css_when_template_updated(
lektor_server.wait_for_build()
assert ".flex" in output_css_path.read_text()

# Give the server a chance to pick up the changes before we start writing
time.sleep(1)
with open(tmp_project_path / "templates/layout.html", "a") as fp:
fp.write("""<div class="before:content-['SENTINEL']"></div>\n""")

Expand All @@ -200,6 +198,8 @@ def test_server_rebuilds_css_when_input_css_updated(
lektor_server.wait_for_build()
assert ".flex" in output_css_path.read_text()

# Give the server a chance to pick up the changes before we start writing
time.sleep(1)
with open(tmp_project_path / "assets/static/style.css", "a") as fp:
fp.write(".SENTINEL { color: red }\n")

Expand Down
Loading