Skip to content

Commit

Permalink
Init command (#113)
Browse files Browse the repository at this point in the history
* feat: init CLI command

Adds a `blurry init` command to start a new project in the current
directory.

Closes #112

* chore: add commands directory

Adds a commands directory to store CLI commands.

Also removes build_development() because it was unnecessary

* chore: update Typer

Updates Typer to support union types like "str | None"
  • Loading branch information
johnfraney authored Jan 7, 2025
1 parent 3916f83 commit 2be3052
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 48 deletions.
30 changes: 13 additions & 17 deletions blurry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from blurry.async_typer import AsyncTyper
from blurry.cli import print_blurry_name
from blurry.cli import print_plugin_table
from blurry.commands.clean import clean_build_directory
from blurry.commands.init import initialize_new_project
from blurry.constants import ENV_VAR_PREFIX
from blurry.images import generate_images_for_srcset
from blurry.markdown import convert_markdown_file_to_html
Expand Down Expand Up @@ -229,15 +231,13 @@ def gather_file_data_by_directory() -> DirectoryFileData:


@app.command(name="clean")
def clean_build_directory():
"""Removes the build directory for a clean build."""
update_settings()
build_directory = get_build_directory()
def clean_command():
clean_build_directory()

try:
shutil.rmtree(build_directory)
except FileNotFoundError:
pass

@app.command(name="init")
def init_command(name: str | None = None, domain: str | None = None):
initialize_new_project(name, domain)


@app.async_command()
Expand Down Expand Up @@ -328,10 +328,6 @@ def on_non_markdown_file_processed(future: concurrent.futures.Future):
print(f"Built site in {difference.total_seconds()} seconds")


async def build_development():
await build(release=False)


async def reload_local_plugins_and_build():
"""Reloads a project's local modules and performs a dev build"""
cwd = str(Path.cwd())
Expand All @@ -343,7 +339,7 @@ async def reload_local_plugins_and_build():
continue
importlib.reload(module)

await build_development()
await build(release=False)


@app.command()
Expand All @@ -358,7 +354,7 @@ def runserver():
SETTINGS["RUNSERVER"] = True

event_loop = asyncio.get_event_loop()
event_loop.create_task(build_development())
event_loop.create_task(build(release=False))

jinja_env = get_jinja_env()

Expand Down Expand Up @@ -392,7 +388,7 @@ def handle_changed_markdown_files(filepaths: list[str]):
)
livereload_server.watch(
f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*",
lambda: event_loop.create_task(build_development()),
lambda: event_loop.create_task(build(release=False)),
ignore=lambda filepath: any(
[
filepath.endswith(".md"),
Expand All @@ -403,15 +399,15 @@ def handle_changed_markdown_files(filepaths: list[str]):
)
livereload_server.watch(
f"{SETTINGS['TEMPLATES_DIRECTORY_NAME']}/**/*",
lambda: event_loop.create_task(build_development()),
lambda: event_loop.create_task(build(release=False)),
ignore=lambda filepath: Path(filepath).is_dir(),
)
livereload_server.watch(
"./**/*.py",
lambda: event_loop.create_task(reload_local_plugins_and_build()),
)
livereload_server.watch(
"blurry.toml", lambda: event_loop.create_task(build_development())
"blurry.toml", lambda: event_loop.create_task(build(release=False))
)
livereload_server.serve(
host=SETTINGS["DEV_HOST"], port=SETTINGS["DEV_PORT"], root=get_build_directory()
Expand Down
Empty file added blurry/commands/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions blurry/commands/clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import shutil

from rich import print

from blurry.settings import get_build_directory
from blurry.settings import update_settings


def clean_build_directory():
"""Removes the build directory for a clean build."""
update_settings()
build_directory = get_build_directory()

try:
shutil.rmtree(build_directory)
except FileNotFoundError:
pass

print(f"Cleaned build directory: {build_directory}")
135 changes: 135 additions & 0 deletions blurry/commands/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import tomllib
from datetime import datetime
from pathlib import Path

from rich.prompt import Prompt

from blurry.settings import get_content_directory
from blurry.settings import get_templates_directory
from blurry.settings import update_settings


BLURRY_CONFIG_TEMPLATE = """
[blurry]
domain = "{domain}"
markdown_file_jinja_template_extension = ".jinja"
[blurry.schema_data.sourceOrganization]
name = "{project_name}"
url = "https://{domain}"
""".strip()

HOMEPAGE_MARKDOWN = """
+++
"@type" = "WebSite"
name = "Home"
abstract = "The homepage of {project_name}, built with Blurry."
datePublished = {date_published}
+++
# Welcome to your new Blurry site
To learn more about how to get started, check out Blurry's quick start docs:
<https://blurry-docs.netlify.app/getting-started/quick-start/>
""".strip()

BASE_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
>
<title>{% block title %}{% endblock %} | {{ sourceOrganization.name }}</title>
<meta name="description" content="{% block description %}{% endblock %}">
<link rel="canonical" href="{{ url }}">
{{ open_graph_tags|safe }}
{{ schema_type_tag|safe }}
</head>
<body>
<nav class="container">
<ul>
<li><strong>{{ sourceOrganization.name }}</strong></li>
</ul>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
<main class="container">
{% block body %}{% endblock %}
</main>
</body>
</html>
"""


WEBSITE_TEMPLATE = """
{% extends "base.jinja" %}
{% block title %}{{ name }}{% endblock %}
{% block description %}{{ abstract }}{% endblock %}
{% block body %}
<article>
{{ body|safe }}
</article>
{% endblock %}
"""


def initialize_new_project(name: str | None, domain: str | None):
update_settings()
blurry_config_file = Path("blurry.toml")
blurry_template_directory = get_templates_directory()
blurry_content_directory = get_content_directory()

if (
blurry_config_file.exists()
or blurry_template_directory.exists()
or blurry_content_directory.exists()
):
print("Blurry project already initialized.")
return
name = name or Prompt.ask(
"What is the name of your company, project, or website?",
)
domain = domain or Prompt.ask("What is your website's domain?")

config_text = BLURRY_CONFIG_TEMPLATE.format(domain=domain, project_name=name)

try:
tomllib.loads(config_text)
except tomllib.TOMLDecodeError as e:
print(f"Error in configuration file: {e}.")
print("Please check your input and try again.")
exit(1)

blurry_config_file.write_text(config_text)

# Write template files
blurry_template_directory.mkdir(exist_ok=True)
base_template_file = blurry_template_directory / "base.jinja"
base_template_file.write_text(BASE_TEMPLATE)

website_template_file = blurry_template_directory / "WebSite.jinja"
website_template_file.write_text(WEBSITE_TEMPLATE)

# Write homepage Markdown file
date_published = datetime.now().strftime("%Y-%m-%d")
blurry_content_directory.mkdir(exist_ok=True)
homepage = blurry_content_directory / "index.md"
homepage.write_text(
HOMEPAGE_MARKDOWN.format(
date_published=date_published,
project_name=name,
)
)

print("Blurry project initialized!")
print("Run 'blurry runserver' to start the dev server.")
24 changes: 22 additions & 2 deletions docs/content/commands/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
name = "Commands: build"
abstract = "Documentation for Blurry's build command"
datePublished = 2023-04-09
dateModified = 2024-01-03
dateModified = 2025-01-07
+++

# Commands: build

## Usage
## Description

`build` builds a production-ready version of a Blurry static site.
It outputs the site in the folder specified by the `build_directory_name` [setting](./../configuration/settings.md), which defaults to `./dist/`
Expand Down Expand Up @@ -101,6 +101,26 @@ dist
18 directories, 19 files
```

## Usage

```shell
$ blurry build
. .
|-.| . ..-..-.. .
`-''-'-'' ' '-|
`-'
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Markdown Plugins ┃ HTML Plugins ┃ Jinja Plugins ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ container │ minify_html │ body_to_cards │
│ punctuation │ │ headings │
│ python_code │ │ url_path │
│ python_code_in_list │ │ blurry_image │
└─────────────────────┴──────────────┴───────────────┘
Blurring 22 Markdown files and 6 other files
Built site in 2.576354 seconds
```

## Options

`clean`: cleans the build directory before building.
Expand Down
43 changes: 43 additions & 0 deletions docs/content/commands/init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
+++
"@type" = "WebPage"
name = "Commands: init"
abstract = "Documentation for Blurry's build command"
datePublished = 2025-01-07
+++

# Commands: init

## Description

The `init` command creates a new Blurry project in the current directory.
It creates everything you need to start working on your site:

- A `blurry.toml` [configuration file](../configuration/blurry.toml.md)
- A [Markdown file](../content/markdown.md) for your site's homepage in `content/index.md`
- A base [template file](../templates/syntax.md) and a `WebSite` template file for your homepage in `templates/`

## Usage

Example:

```shell
$ blurry init
What is the name of your company, project, or website?: Blurry
What is your website's domain?: blurry-dev.netlify.app
Blurry project initialized!
Run 'blurry runserver' to start the dev server.
```
## Options
`name`: Your project's name

`domain`: Your project's domain
Example:
```shell
$ blurry init --name "Blurry" --domain "blurry-docs.netlify.app"
Blurry project initialized!
Run 'blurry runserver' to start the dev server.
```
Loading

0 comments on commit 2be3052

Please sign in to comment.