diff --git a/blurry/__init__.py b/blurry/__init__.py index d2cf1ef..cccf180 100644 --- a/blurry/__init__.py +++ b/blurry/__init__.py @@ -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 @@ -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() @@ -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()) @@ -343,7 +339,7 @@ async def reload_local_plugins_and_build(): continue importlib.reload(module) - await build_development() + await build(release=False) @app.command() @@ -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() @@ -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"), @@ -403,7 +399,7 @@ 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( @@ -411,7 +407,7 @@ def handle_changed_markdown_files(filepaths: list[str]): 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() diff --git a/blurry/commands/__init__.py b/blurry/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/blurry/commands/clean.py b/blurry/commands/clean.py new file mode 100644 index 0000000..5de9153 --- /dev/null +++ b/blurry/commands/clean.py @@ -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}") diff --git a/blurry/commands/init.py b/blurry/commands/init.py new file mode 100644 index 0000000..8f1a4e6 --- /dev/null +++ b/blurry/commands/init.py @@ -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: + + +""".strip() + +BASE_TEMPLATE = """ + + + + + + + + {% block title %}{% endblock %} | {{ sourceOrganization.name }} + + + {{ open_graph_tags|safe }} + {{ schema_type_tag|safe }} + + + + +
+ {% block body %}{% endblock %} +
+ + +""" + + +WEBSITE_TEMPLATE = """ +{% extends "base.jinja" %} + +{% block title %}{{ name }}{% endblock %} +{% block description %}{{ abstract }}{% endblock %} + +{% block body %} +
+ {{ body|safe }} +
+{% 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.") diff --git a/docs/content/commands/build.md b/docs/content/commands/build.md index 9720f3d..fcf3baa 100644 --- a/docs/content/commands/build.md +++ b/docs/content/commands/build.md @@ -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/` @@ -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. diff --git a/docs/content/commands/init.md b/docs/content/commands/init.md new file mode 100644 index 0000000..367c027 --- /dev/null +++ b/docs/content/commands/init.md @@ -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. +``` diff --git a/docs/poetry.lock b/docs/poetry.lock index 9b6ed11..3277128 100644 --- a/docs/poetry.lock +++ b/docs/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -33,7 +33,7 @@ pydantic2-schemaorg = "^0.2.0" PyLD = "^2.0.3" rich = "^13.3.3" selectolax = "^0.3.27" -typer = "^0.6.1" +typer = "^0.15.1" Wand = "^0.6.6" [package.source] @@ -654,13 +654,13 @@ pydantic = ">=2.9.2,<3.0.0" [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -784,6 +784,17 @@ files = [ [package.extras] cython = ["Cython (==3.0.11)"] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "tornado" version = "6.4.2" @@ -806,23 +817,20 @@ files = [ [[package]] name = "typer" -version = "0.6.1" +version = "0.15.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "typer-0.6.1-py3-none-any.whl", hash = "sha256:54b19e5df18654070a82f8c2aa1da456a4ac16a2a83e6dcd9f170e291c56338e"}, - {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"}, + {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, + {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, ] [package.dependencies] -click = ">=7.1.1,<9.0.0" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" [[package]] name = "typing-extensions" diff --git a/docs/templates/WebPage.html.jinja b/docs/templates/WebPage.html.jinja index 4682342..d8f1502 100644 --- a/docs/templates/WebPage.html.jinja +++ b/docs/templates/WebPage.html.jinja @@ -35,8 +35,10 @@
Commands
diff --git a/poetry.lock b/poetry.lock index ac6d349..698a419 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1182,6 +1182,17 @@ files = [ [package.extras] cython = ["Cython (==3.0.11)"] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "toml" version = "0.10.2" @@ -1215,23 +1226,20 @@ files = [ [[package]] name = "typer" -version = "0.6.1" +version = "0.15.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "typer-0.6.1-py3-none-any.whl", hash = "sha256:54b19e5df18654070a82f8c2aa1da456a4ac16a2a83e6dcd9f170e291c56338e"}, - {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"}, + {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, + {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, ] [package.dependencies] -click = ">=7.1.1,<9.0.0" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" [[package]] name = "typing-extensions" @@ -1324,4 +1332,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "6b9769c7c71be685c87333cf72d998b3ecb16205e79dc91fcdf94015ea54d254" +content-hash = "6f442b6993816e3148089f5730b8439ce307e5d14a6ec0cf4b868efbed13442e" diff --git a/pyproject.toml b/pyproject.toml index 5eb0062..7ba77e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ mistune = "^3.0.10" python = "^3.11" rich = "^13.3.3" selectolax = "^0.3.27" -typer = "^0.6.1" +typer = "^0.15.1" htmlmin2 = "^0.1.13" pydantic2-schemaorg = "^0.2.0" dpath = "^2.1.6"