Skip to content

Commit 2314689

Browse files
committed
Add PyCharm workaround commands for Tailwind CLI
Introduce `install_pycharm_workaround` and `uninstall_pycharm_workaround` management commands to facilitate the integration of Tailwind CLI with PyCharm.
1 parent c98c91f commit 2314689

File tree

7 files changed

+191
-6
lines changed

7 files changed

+191
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 2.21.0
44

5+
- Added `install_pycharm_workaround` and `remove_pycharm_workaround` management commands. [#142](https://github.com/django-commons/django-tailwind-cli/issues/142)
56
- Added `remove_cli` subcommand to remove the CLI. [#132](https://github.com/django-commons/django-tailwind-cli/issues/132)
67
- Refactored all the class based management command into simpler function based commands.
78
- Refactored the process to build the runserver command.

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,16 @@ Checkout the detailed [installation guide](https://django-tailwind-cli.rtfd.io/l
6363

6464
- Simplest possible integration.
6565
- Management commands:
66-
- To start the Tailwind CLI in watch mode during development.
67-
- To build the production grade CSS file for your project.
68-
- To start a debug server along with the Tailwind CLI in watch mode in a single session.
69-
- Necessary configuration to adapt the library to your project, when the defaults don't fit you.
66+
67+
* To start the Tailwind CLI in watch mode during development.
68+
* To build the production grade CSS file for your project.
69+
* To start a debug server along with the Tailwind CLI in watch mode in a single session.
70+
71+
- Configuration options to adapt the library to your project, when the defaults don't fit you.
7072
- A template tag to include the Tailwind CSS file in your project.
7173
- A base template for your project.
7274
- A sane tailwind.config.js that activates all the official plugins and includes a simple HTMX plugin.
75+
- A management command to install [a workaround to support Tailwind CLI in PyCharm](https://django-tailwind-cli.rtfd.io/usage/#use-with-pycharm).
7376

7477
## Requirements
7578

docs/installation.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ hide:
7878

7979
## Optional steps
8080

81+
### Install PyCharm workaround
82+
83+
In order [to use Tailwind CLI with PyCharm](/usage/#use-with-pycharm) you have to install a workaround.
84+
85+
```shell
86+
python manage.py tailwind install_pycharm_workaround
87+
```
88+
8189
### Install `django-browser-reload`
8290

8391
If you enjoy automatic reloading during development. Install the [django-browser-reload](https://github.com/adamchainz/django-browser-reload) app. The following installation steps are taken from the README of the project.

docs/usage.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ Options:
9292
9393
Run `python manage.py tailwind watch` to just start a tailwind watcher process if you prefer to start your debug server in a seperate shell or prefer a different solution than runserver or runserver_plus.
9494
95+
## Use with PyCharm
96+
97+
[PyCharm](https://www.jetbrains.com/pycharm/) is a great IDE, but its Tailwind CSS plugin doesn't work well with the standalone CLI this package uses. Luckily, there is a [workaround](https://youtrack.jetbrains.com/issue/WEB-55647/Support-Tailwind-css-autocompletion-using-standalone-tailwind-CLI#focus=Comments-27-10957961.0-0) for this.
98+
99+
### install_pycharm_workaround
100+
101+
Run `python manage.py tailwind install_pycharm_workaround` to install the workaround in your project.
102+
103+
!!! info "Update .gitignore file"
104+
105+
106+
Update your `.gitignore` manually so that it exludes the created `package.json` file and the `node_modules` directory, that is created by this management command.
107+
108+
```gitignore
109+
package.json
110+
node_modules/
111+
```
112+
113+
### remove_pycharm_workaround
114+
115+
Run `python manage.py tailwind install_pycharm_workaround` to remove the workaround from your project.
116+
95117
## Use with Docker Compose
96118

97119
When used in the `watch` mode, the Tailwind CLI requires a TTY-enabled environment to function correctly. In a Docker Compose setup, ensure that the container executing the Tailwind style rebuild command (either `python manage.py tailwind runserver` or `python manage.py tailwind watch`, as noted above) is configured with the `tty: true` setting in your `docker-compose.yml`.

src/django_tailwind_cli/management/commands/tailwind.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import importlib.util
44
import os
5+
import shutil
56
import subprocess
67
import sys
78
from multiprocessing import Process
@@ -242,7 +243,77 @@ def runserver_plus(
242243
)
243244

244245

245-
# UTILITY FUNCTIONS --------------------------------------------------------------------------------
246+
@app.command(name="install_pycharm_workaround")
247+
def install_pycharm_workaround():
248+
"""
249+
Configures the workarounds for PyCharm to get tailwind plugin to work with the tailwind CLI.
250+
"""
251+
_validate_config()
252+
_download_cli()
253+
254+
package_json = settings.BASE_DIR / "package.json"
255+
package_json_content = '{"devDependencies": {"tailwindcss": "latest"}}'
256+
cli_js = settings.BASE_DIR / "node_modules" / "tailwindcss" / "lib" / "cli.js"
257+
258+
if package_json.exists():
259+
if package_json.read_text() == package_json_content:
260+
typer.secho(
261+
f"PyCharm workaround is already installed at '{package_json}'.",
262+
fg=typer.colors.GREEN,
263+
)
264+
return
265+
else:
266+
typer.secho(
267+
f"Found an existing package.json at '{package_json}' that is " "not compatible.",
268+
fg=typer.colors.YELLOW,
269+
)
270+
return
271+
else:
272+
package_json.write_text(package_json_content)
273+
typer.secho(
274+
f"Created package.json at '{package_json}'",
275+
fg=typer.colors.GREEN,
276+
)
277+
278+
cli_js.parent.mkdir(parents=True, exist_ok=True)
279+
cli_path = utils.get_full_cli_path()
280+
cli_js.symlink_to(cli_path)
281+
typer.secho(
282+
f"Created link at '{cli_js}' to '{cli_path}'.",
283+
fg=typer.colors.GREEN,
284+
)
285+
typer.secho(
286+
"\nAssure that you have added package.json and node_modules to your .gitignore file.",
287+
fg=typer.colors.YELLOW,
288+
)
289+
290+
291+
@app.command(name="uninstall_pycharm_workaround")
292+
def uninstall_pycharm_workaround():
293+
package_json = settings.BASE_DIR / "package.json"
294+
package_json_content = '{"devDependencies": {"tailwindcss": "latest"}}'
295+
node_modules = settings.BASE_DIR / "node_modules"
296+
297+
if package_json.exists() and package_json.read_text() == package_json_content:
298+
package_json.unlink()
299+
shutil.rmtree(node_modules)
300+
typer.secho(
301+
"Removed package.json and cli.js.",
302+
fg=typer.colors.GREEN,
303+
)
304+
elif package_json.exists() and package_json.read_text() != package_json_content:
305+
typer.secho(
306+
f"Found an existing package.json at '{package_json}' was not installed by us.",
307+
fg=typer.colors.YELLOW,
308+
)
309+
else:
310+
typer.secho(
311+
"No package.json or cli.js found.",
312+
fg=typer.colors.YELLOW,
313+
)
314+
315+
316+
# UTILITY FUNCTIONS -------------------------------------------------------------------------------
246317

247318

248319
def _validate_config():

tests/test_management_commands.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,83 @@ def test_list_projecttest_list_project_all_templates_templates(capsys, settings)
314314
assert "templates/tailwind_cli/tailwind_css.html" in captured.out
315315
assert "templates/tests/base.html" in captured.out
316316
assert "templates/admin" in captured.out
317+
318+
319+
def test_install_pycharm_workaround(settings, tmp_path, capsys):
320+
settings.BASE_DIR = tmp_path
321+
settings.TAILWIND_CLI_PATH = str(tmp_path)
322+
package_json = settings.BASE_DIR / "package.json"
323+
cli_js = settings.BASE_DIR / "node_modules" / "tailwindcss" / "lib" / "cli.js"
324+
325+
assert not package_json.exists()
326+
assert not cli_js.exists()
327+
328+
call_command("tailwind", "install_pycharm_workaround")
329+
330+
assert package_json.exists()
331+
assert (
332+
settings.BASE_DIR / "package.json"
333+
).read_text() == '{"devDependencies": {"tailwindcss": "latest"}}'
334+
captured = capsys.readouterr()
335+
assert "Created package.json" in captured.out
336+
337+
assert cli_js.exists()
338+
assert cli_js.resolve() == utils.get_full_cli_path()
339+
assert "Created link at" in captured.out
340+
341+
assert (
342+
"Assure that you have added package.json and node_modules to your .gitignore file."
343+
in captured.out
344+
)
345+
346+
347+
def test_install_pycharm_workaround_twice(settings, tmp_path, capsys):
348+
settings.BASE_DIR = tmp_path
349+
settings.TAILWIND_CLI_PATH = str(tmp_path)
350+
call_command("tailwind", "install_pycharm_workaround")
351+
call_command("tailwind", "install_pycharm_workaround")
352+
captured = capsys.readouterr()
353+
assert "PyCharm workaround is already installed at" in captured.out
354+
355+
356+
def test_install_pycharm_workaround_with_existing(settings, tmp_path, capsys):
357+
settings.BASE_DIR = tmp_path
358+
settings.TAILWIND_CLI_PATH = str(tmp_path)
359+
360+
package_json = settings.BASE_DIR / "package.json"
361+
package_json.write_text("{}")
362+
363+
call_command("tailwind", "install_pycharm_workaround")
364+
captured = capsys.readouterr()
365+
assert "Found an existing package.json at" in captured.out
366+
assert "that is not compatible." in captured.out
367+
368+
369+
def test_uninstall_pycharm_workaround(settings, tmp_path, capsys):
370+
settings.BASE_DIR = tmp_path
371+
settings.TAILWIND_CLI_PATH = str(tmp_path)
372+
call_command("tailwind", "install_pycharm_workaround")
373+
call_command("tailwind", "uninstall_pycharm_workaround")
374+
captured = capsys.readouterr()
375+
assert "Removed package.json and cli.js." in captured.out
376+
377+
378+
def test_uninstall_pycharm_workaround_with_other_package_json(settings, tmp_path, capsys):
379+
settings.BASE_DIR = tmp_path
380+
settings.TAILWIND_CLI_PATH = str(tmp_path)
381+
382+
package_json = settings.BASE_DIR / "package.json"
383+
package_json.write_text("{}")
384+
385+
call_command("tailwind", "uninstall_pycharm_workaround")
386+
captured = capsys.readouterr()
387+
assert "Found an existing package.json at" in captured.out
388+
assert "was not installed by us." in captured.out
389+
390+
391+
def test_uninstall_pycharm_workaround_without_install(settings, tmp_path, capsys):
392+
settings.BASE_DIR = tmp_path
393+
settings.TAILWIND_CLI_PATH = str(tmp_path)
394+
call_command("tailwind", "uninstall_pycharm_workaround")
395+
captured = capsys.readouterr()
396+
assert "No package.json or cli.js found." in captured.out

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)