Skip to content

Commit

Permalink
Add PyCharm workaround commands for Tailwind CLI
Browse files Browse the repository at this point in the history
Introduce `install_pycharm_workaround` and `uninstall_pycharm_workaround` management commands to facilitate the integration of Tailwind CLI with PyCharm.
  • Loading branch information
oliverandrich committed Dec 7, 2024
1 parent c98c91f commit 2314689
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 2.21.0

- Added `install_pycharm_workaround` and `remove_pycharm_workaround` management commands. [#142](https://github.com/django-commons/django-tailwind-cli/issues/142)
- Added `remove_cli` subcommand to remove the CLI. [#132](https://github.com/django-commons/django-tailwind-cli/issues/132)
- Refactored all the class based management command into simpler function based commands.
- Refactored the process to build the runserver command.
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,16 @@ Checkout the detailed [installation guide](https://django-tailwind-cli.rtfd.io/l

- Simplest possible integration.
- Management commands:
- To start the Tailwind CLI in watch mode during development.
- To build the production grade CSS file for your project.
- To start a debug server along with the Tailwind CLI in watch mode in a single session.
- Necessary configuration to adapt the library to your project, when the defaults don't fit you.

* To start the Tailwind CLI in watch mode during development.
* To build the production grade CSS file for your project.
* To start a debug server along with the Tailwind CLI in watch mode in a single session.

- Configuration options to adapt the library to your project, when the defaults don't fit you.
- A template tag to include the Tailwind CSS file in your project.
- A base template for your project.
- A sane tailwind.config.js that activates all the official plugins and includes a simple HTMX plugin.
- A management command to install [a workaround to support Tailwind CLI in PyCharm](https://django-tailwind-cli.rtfd.io/usage/#use-with-pycharm).

## Requirements

Expand Down
8 changes: 8 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ hide:

## Optional steps

### Install PyCharm workaround

In order [to use Tailwind CLI with PyCharm](/usage/#use-with-pycharm) you have to install a workaround.

```shell
python manage.py tailwind install_pycharm_workaround
```

### Install `django-browser-reload`

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.
Expand Down
22 changes: 22 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,28 @@ Options:
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.
## Use with PyCharm
[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.

### install_pycharm_workaround

Run `python manage.py tailwind install_pycharm_workaround` to install the workaround in your project.

!!! info "Update .gitignore file"


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.

```gitignore
package.json
node_modules/
```

### remove_pycharm_workaround

Run `python manage.py tailwind install_pycharm_workaround` to remove the workaround from your project.

## Use with Docker Compose

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`.
Expand Down
73 changes: 72 additions & 1 deletion src/django_tailwind_cli/management/commands/tailwind.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import importlib.util
import os
import shutil
import subprocess
import sys
from multiprocessing import Process
Expand Down Expand Up @@ -242,7 +243,77 @@ def runserver_plus(
)


# UTILITY FUNCTIONS --------------------------------------------------------------------------------
@app.command(name="install_pycharm_workaround")
def install_pycharm_workaround():
"""
Configures the workarounds for PyCharm to get tailwind plugin to work with the tailwind CLI.
"""
_validate_config()
_download_cli()

package_json = settings.BASE_DIR / "package.json"
package_json_content = '{"devDependencies": {"tailwindcss": "latest"}}'
cli_js = settings.BASE_DIR / "node_modules" / "tailwindcss" / "lib" / "cli.js"

if package_json.exists():
if package_json.read_text() == package_json_content:
typer.secho(
f"PyCharm workaround is already installed at '{package_json}'.",
fg=typer.colors.GREEN,
)
return
else:
typer.secho(
f"Found an existing package.json at '{package_json}' that is " "not compatible.",
fg=typer.colors.YELLOW,
)
return
else:
package_json.write_text(package_json_content)
typer.secho(
f"Created package.json at '{package_json}'",
fg=typer.colors.GREEN,
)

cli_js.parent.mkdir(parents=True, exist_ok=True)
cli_path = utils.get_full_cli_path()
cli_js.symlink_to(cli_path)
typer.secho(
f"Created link at '{cli_js}' to '{cli_path}'.",
fg=typer.colors.GREEN,
)
typer.secho(
"\nAssure that you have added package.json and node_modules to your .gitignore file.",
fg=typer.colors.YELLOW,
)


@app.command(name="uninstall_pycharm_workaround")
def uninstall_pycharm_workaround():
package_json = settings.BASE_DIR / "package.json"
package_json_content = '{"devDependencies": {"tailwindcss": "latest"}}'
node_modules = settings.BASE_DIR / "node_modules"

if package_json.exists() and package_json.read_text() == package_json_content:
package_json.unlink()
shutil.rmtree(node_modules)
typer.secho(
"Removed package.json and cli.js.",
fg=typer.colors.GREEN,
)
elif package_json.exists() and package_json.read_text() != package_json_content:
typer.secho(
f"Found an existing package.json at '{package_json}' was not installed by us.",
fg=typer.colors.YELLOW,
)
else:
typer.secho(
"No package.json or cli.js found.",
fg=typer.colors.YELLOW,
)


# UTILITY FUNCTIONS -------------------------------------------------------------------------------


def _validate_config():
Expand Down
80 changes: 80 additions & 0 deletions tests/test_management_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,83 @@ def test_list_projecttest_list_project_all_templates_templates(capsys, settings)
assert "templates/tailwind_cli/tailwind_css.html" in captured.out
assert "templates/tests/base.html" in captured.out
assert "templates/admin" in captured.out


def test_install_pycharm_workaround(settings, tmp_path, capsys):
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = str(tmp_path)
package_json = settings.BASE_DIR / "package.json"
cli_js = settings.BASE_DIR / "node_modules" / "tailwindcss" / "lib" / "cli.js"

assert not package_json.exists()
assert not cli_js.exists()

call_command("tailwind", "install_pycharm_workaround")

assert package_json.exists()
assert (
settings.BASE_DIR / "package.json"
).read_text() == '{"devDependencies": {"tailwindcss": "latest"}}'
captured = capsys.readouterr()
assert "Created package.json" in captured.out

assert cli_js.exists()
assert cli_js.resolve() == utils.get_full_cli_path()
assert "Created link at" in captured.out

assert (
"Assure that you have added package.json and node_modules to your .gitignore file."
in captured.out
)


def test_install_pycharm_workaround_twice(settings, tmp_path, capsys):
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = str(tmp_path)
call_command("tailwind", "install_pycharm_workaround")
call_command("tailwind", "install_pycharm_workaround")
captured = capsys.readouterr()
assert "PyCharm workaround is already installed at" in captured.out


def test_install_pycharm_workaround_with_existing(settings, tmp_path, capsys):
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = str(tmp_path)

package_json = settings.BASE_DIR / "package.json"
package_json.write_text("{}")

call_command("tailwind", "install_pycharm_workaround")
captured = capsys.readouterr()
assert "Found an existing package.json at" in captured.out
assert "that is not compatible." in captured.out


def test_uninstall_pycharm_workaround(settings, tmp_path, capsys):
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = str(tmp_path)
call_command("tailwind", "install_pycharm_workaround")
call_command("tailwind", "uninstall_pycharm_workaround")
captured = capsys.readouterr()
assert "Removed package.json and cli.js." in captured.out


def test_uninstall_pycharm_workaround_with_other_package_json(settings, tmp_path, capsys):
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = str(tmp_path)

package_json = settings.BASE_DIR / "package.json"
package_json.write_text("{}")

call_command("tailwind", "uninstall_pycharm_workaround")
captured = capsys.readouterr()
assert "Found an existing package.json at" in captured.out
assert "was not installed by us." in captured.out


def test_uninstall_pycharm_workaround_without_install(settings, tmp_path, capsys):
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = str(tmp_path)
call_command("tailwind", "uninstall_pycharm_workaround")
captured = capsys.readouterr()
assert "No package.json or cli.js found." in captured.out
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2314689

Please sign in to comment.