blurry-cli 0.22.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. blurry_cli-0.22.0/LICENSE +21 -0
  2. blurry_cli-0.22.0/PKG-INFO +92 -0
  3. blurry_cli-0.22.0/README.md +51 -0
  4. blurry_cli-0.22.0/blurry/__init__.py +227 -0
  5. blurry_cli-0.22.0/blurry/__main__.py +3 -0
  6. blurry_cli-0.22.0/blurry/async_typer.py +20 -0
  7. blurry_cli-0.22.0/blurry/cli.py +37 -0
  8. blurry_cli-0.22.0/blurry/commands/__init__.py +0 -0
  9. blurry_cli-0.22.0/blurry/commands/clean.py +19 -0
  10. blurry_cli-0.22.0/blurry/commands/init.py +135 -0
  11. blurry_cli-0.22.0/blurry/commands/version.py +9 -0
  12. blurry_cli-0.22.0/blurry/constants.py +7 -0
  13. blurry_cli-0.22.0/blurry/file_processors/__init__.py +156 -0
  14. blurry_cli-0.22.0/blurry/gather_file_data_by_directory.py +60 -0
  15. blurry_cli-0.22.0/blurry/images.py +125 -0
  16. blurry_cli-0.22.0/blurry/markdown/__init__.py +227 -0
  17. blurry_cli-0.22.0/blurry/markdown/front_matter.py +56 -0
  18. blurry_cli-0.22.0/blurry/markdown/renderer_functions/__init__.py +0 -0
  19. blurry_cli-0.22.0/blurry/markdown/renderer_functions/render_video.py +34 -0
  20. blurry_cli-0.22.0/blurry/open_graph.py +57 -0
  21. blurry_cli-0.22.0/blurry/plugins/__init__.py +6 -0
  22. blurry_cli-0.22.0/blurry/plugins/html_plugins/__init__.py +0 -0
  23. blurry_cli-0.22.0/blurry/plugins/html_plugins/minify_html_plugin.py +37 -0
  24. blurry_cli-0.22.0/blurry/plugins/jinja_plugins/__init__.py +0 -0
  25. blurry_cli-0.22.0/blurry/plugins/jinja_plugins/blurry_image_extension.py +75 -0
  26. blurry_cli-0.22.0/blurry/plugins/jinja_plugins/filters.py +43 -0
  27. blurry_cli-0.22.0/blurry/plugins/markdown_plugins/__init__.py +0 -0
  28. blurry_cli-0.22.0/blurry/plugins/markdown_plugins/container_plugin.py +40 -0
  29. blurry_cli-0.22.0/blurry/plugins/markdown_plugins/punctuation_plugin.py +25 -0
  30. blurry_cli-0.22.0/blurry/plugins/markdown_plugins/python_code_plugin.py +49 -0
  31. blurry_cli-0.22.0/blurry/py.typed +0 -0
  32. blurry_cli-0.22.0/blurry/runserver_handlers.py +37 -0
  33. blurry_cli-0.22.0/blurry/schema_validation/__init__.py +38 -0
  34. blurry_cli-0.22.0/blurry/schema_validation/models.py +574 -0
  35. blurry_cli-0.22.0/blurry/settings.py +99 -0
  36. blurry_cli-0.22.0/blurry/sitemap.py +49 -0
  37. blurry_cli-0.22.0/blurry/templates.py +47 -0
  38. blurry_cli-0.22.0/blurry/types.py +36 -0
  39. blurry_cli-0.22.0/blurry/utils.py +124 -0
  40. blurry_cli-0.22.0/pyproject.toml +88 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 John Franey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: blurry-cli
3
+ Version: 0.22.0
4
+ Summary: A Mistune-based static site generator for Python
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: static-site-generator,seo,pagespeed
8
+ Author: John Franey
9
+ Author-email: franey@duck.com
10
+ Requires-Python: >=3.11,<4.0
11
+ Classifier: Environment :: Console
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System
20
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Text Processing :: Markup :: HTML
23
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
24
+ Requires-Dist: Jinja2 (>=3.1.5,<4.0.0)
25
+ Requires-Dist: PyLD (>=2.0.4,<3.0.0)
26
+ Requires-Dist: dpath (>=2.2.0,<3.0.0)
27
+ Requires-Dist: htmlmin2 (>=0.1.13,<0.2.0)
28
+ Requires-Dist: jinja2-simple-tags (>=0.6.1,<0.7.0)
29
+ Requires-Dist: jinjax (>=0.48,<0.64)
30
+ Requires-Dist: livereload (>=2.7.1,<3.0.0)
31
+ Requires-Dist: mistune (>=3.1.0,<4.0.0)
32
+ Requires-Dist: pillow (>=12.0.0,<13.0.0)
33
+ Requires-Dist: pydantic (>=2.10.5,<3.0.0)
34
+ Requires-Dist: pymediainfo (>=7.0.1,<8.0.0)
35
+ Requires-Dist: rich (>=13.9.4,<15.0.0)
36
+ Requires-Dist: selectolax (>=0.3.27,<0.5.0)
37
+ Requires-Dist: typer (>=0.15.1,<0.22.0)
38
+ Project-URL: Repository, https://github.com/blurry-dev/blurry
39
+ Description-Content-Type: text/markdown
40
+
41
+ # Blurry
42
+
43
+ [![Nox build status](https://github.com/blurry-dev/blurry/actions/workflows/github-actions-nox.yml/badge.svg?branch=main)](https://github.com/blurry-dev/blurry/actions/workflows/github-actions-nox.yml)
44
+ [![PyPI package version](https://img.shields.io/pypi/v/blurry-cli.svg)](https://pypi.org/project/blurry-cli/)
45
+ [![PyPI Python versions](https://img.shields.io/pypi/pyversions/blurry-cli.svg)](https://pypi.org/project/blurry-cli/)
46
+ [![PyPI downloads per month](https://img.shields.io/pypi/dm/blurry-cli.svg)](https://pypi.python.org/pypi/blurry-cli/)
47
+ ![Package license](https://img.shields.io/github/license/blurry-dev/blurry.svg)
48
+
49
+ ![Blurry logo](https://github.com/blurry-dev/blurry/raw/main/docs/content/favicon.png)
50
+
51
+ ## tl;dr
52
+
53
+ Blurry is a schema-first, plugin-enabled static site generator.
54
+ Markdown front matter directly to [Schema.org types](https://schema.org/docs/full.html), so your content is SEO-friendly and [rich results-ready](https://developers.google.com/search/docs/appearance/structured-data/search-gallery) out of the box.
55
+
56
+ Blurry also makes your images responsive, supports embedding Python source code in Markdown files, and more.
57
+
58
+ Check out the docs and try it out!
59
+
60
+ **Note**: Until v1.0.0, minor versions (0.x.0) may not be backwards compatible.
61
+
62
+ ## Documentation
63
+
64
+ View the documentation site at <https://blurry-docs.netlify.app/>.
65
+
66
+ ## Contributing
67
+
68
+ Contributions are welcome!
69
+ Check out [the contribution docs](https://github.com/blurry-dev/blurry/blob/main/CONTRIBUTING.md) to get started.
70
+
71
+ ## Security contact information
72
+
73
+ To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
74
+ Tidelift will coordinate the fix and disclosure.
75
+
76
+ ## Standing on the shoulders of giants
77
+
78
+ Blurry blends together high-quality libraries:
79
+
80
+ - [Mistune](https://mistune.readthedocs.io/) to convert Markdown to HTML
81
+ - [Jinja](https://jinja.palletsprojects.com/) for HTML templating
82
+ - [JinjaX](https://jinjax.scaletti.dev/) for Jinja components
83
+ - [LiveReload](https://livereload.readthedocs.io/) for an HTTP server with automatic browser reloading
84
+ - [Typer](https://typer.tiangolo.com/) for its CLI interface
85
+ - [Pillow](https://pillow.readthedocs.io/en/stable/)
86
+
87
+ &copy; 2020-present, John Franey
88
+
89
+ ## Star History
90
+
91
+ [![Star History Chart](https://api.star-history.com/svg?repos=blurry-dev/blurry&type=date&legend=top-left)](https://www.star-history.com/#blurry-dev/blurry&type=date&legend=top-left)
92
+
@@ -0,0 +1,51 @@
1
+ # Blurry
2
+
3
+ [![Nox build status](https://github.com/blurry-dev/blurry/actions/workflows/github-actions-nox.yml/badge.svg?branch=main)](https://github.com/blurry-dev/blurry/actions/workflows/github-actions-nox.yml)
4
+ [![PyPI package version](https://img.shields.io/pypi/v/blurry-cli.svg)](https://pypi.org/project/blurry-cli/)
5
+ [![PyPI Python versions](https://img.shields.io/pypi/pyversions/blurry-cli.svg)](https://pypi.org/project/blurry-cli/)
6
+ [![PyPI downloads per month](https://img.shields.io/pypi/dm/blurry-cli.svg)](https://pypi.python.org/pypi/blurry-cli/)
7
+ ![Package license](https://img.shields.io/github/license/blurry-dev/blurry.svg)
8
+
9
+ ![Blurry logo](https://github.com/blurry-dev/blurry/raw/main/docs/content/favicon.png)
10
+
11
+ ## tl;dr
12
+
13
+ Blurry is a schema-first, plugin-enabled static site generator.
14
+ Markdown front matter directly to [Schema.org types](https://schema.org/docs/full.html), so your content is SEO-friendly and [rich results-ready](https://developers.google.com/search/docs/appearance/structured-data/search-gallery) out of the box.
15
+
16
+ Blurry also makes your images responsive, supports embedding Python source code in Markdown files, and more.
17
+
18
+ Check out the docs and try it out!
19
+
20
+ **Note**: Until v1.0.0, minor versions (0.x.0) may not be backwards compatible.
21
+
22
+ ## Documentation
23
+
24
+ View the documentation site at <https://blurry-docs.netlify.app/>.
25
+
26
+ ## Contributing
27
+
28
+ Contributions are welcome!
29
+ Check out [the contribution docs](https://github.com/blurry-dev/blurry/blob/main/CONTRIBUTING.md) to get started.
30
+
31
+ ## Security contact information
32
+
33
+ To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
34
+ Tidelift will coordinate the fix and disclosure.
35
+
36
+ ## Standing on the shoulders of giants
37
+
38
+ Blurry blends together high-quality libraries:
39
+
40
+ - [Mistune](https://mistune.readthedocs.io/) to convert Markdown to HTML
41
+ - [Jinja](https://jinja.palletsprojects.com/) for HTML templating
42
+ - [JinjaX](https://jinjax.scaletti.dev/) for Jinja components
43
+ - [LiveReload](https://livereload.readthedocs.io/) for an HTTP server with automatic browser reloading
44
+ - [Typer](https://typer.tiangolo.com/) for its CLI interface
45
+ - [Pillow](https://pillow.readthedocs.io/en/stable/)
46
+
47
+ &copy; 2020-present, John Franey
48
+
49
+ ## Star History
50
+
51
+ [![Star History Chart](https://api.star-history.com/svg?repos=blurry-dev/blurry&type=date&legend=top-left)](https://www.star-history.com/#blurry-dev/blurry&type=date&legend=top-left)
@@ -0,0 +1,227 @@
1
+ import asyncio
2
+ import concurrent.futures
3
+ import importlib
4
+ import os
5
+ import pkgutil
6
+ import sys
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ from livereload import Server
11
+ from rich import print
12
+ from rich.console import Console
13
+ from rich.progress import BarColumn
14
+ from rich.progress import Progress
15
+ from rich.progress import TaskProgressColumn
16
+ from rich.progress import TextColumn
17
+
18
+ from blurry.async_typer import AsyncTyper
19
+ from blurry.cli import print_blurry_name
20
+ from blurry.cli import print_plugin_table
21
+ from blurry.commands.clean import clean_build_directory
22
+ from blurry.commands.init import initialize_new_project
23
+ from blurry.commands.version import print_blurry_version
24
+ from blurry.constants import ENV_VAR_PREFIX
25
+ from blurry.file_processors import process_non_markdown_file
26
+ from blurry.file_processors import write_html_file
27
+ from blurry.gather_file_data_by_directory import gather_file_data_by_directory
28
+ from blurry.runserver_handlers import handle_changed_jinja_files
29
+ from blurry.runserver_handlers import handle_changed_markdown_files
30
+ from blurry.runserver_handlers import rebuild_markdown_files
31
+ from blurry.settings import get_build_directory
32
+ from blurry.settings import get_content_directory
33
+ from blurry.settings import get_settings
34
+ from blurry.settings import update_settings
35
+ from blurry.sitemap import write_sitemap_file
36
+ from blurry.templates import get_jinja_env
37
+ from blurry.utils import handle_future_result
38
+
39
+
40
+ warning_console = Console(stderr=True, style="bold yellow")
41
+
42
+
43
+ app = AsyncTyper(no_args_is_help=True)
44
+
45
+
46
+ @app.command(name="clean")
47
+ def clean_command():
48
+ clean_build_directory()
49
+
50
+
51
+ @app.command(name="init")
52
+ def init_command(name: str | None = None, domain: str | None = None):
53
+ initialize_new_project(name, domain)
54
+
55
+
56
+ @app.command(name="version")
57
+ def version_command():
58
+ print_blurry_version()
59
+
60
+
61
+ @app.async_command()
62
+ async def build(release=True, clean: bool = False):
63
+ """Generates HTML content from Markdown files."""
64
+ start = datetime.now()
65
+
66
+ if release:
67
+ update_settings()
68
+ print_blurry_name()
69
+ print_plugin_table()
70
+
71
+ build_directory = get_build_directory()
72
+
73
+ if clean:
74
+ clean_build_directory()
75
+
76
+ build_directory.mkdir(parents=True, exist_ok=True)
77
+
78
+ content_directory = get_content_directory()
79
+ build_directory.mkdir(parents=True, exist_ok=True)
80
+ jinja_env = get_jinja_env()
81
+
82
+ os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "prod" if release else "dev")
83
+
84
+ file_data_by_directory = gather_file_data_by_directory()
85
+
86
+ markdown_task_count = 0
87
+ non_markdown_task_count = 1 # sitemap
88
+
89
+ with Progress(
90
+ TextColumn("[progress.description]{task.description}"),
91
+ BarColumn(),
92
+ TaskProgressColumn(),
93
+ transient=True,
94
+ ) as progress:
95
+ with concurrent.futures.ThreadPoolExecutor() as executor:
96
+ markdown_task = progress.add_task("[blue]Markdown")
97
+ other_task = progress.add_task("[blue]Other")
98
+
99
+ sitemap_future = executor.submit(write_sitemap_file, file_data_by_directory)
100
+ sitemap_future.add_done_callback(
101
+ lambda future: handle_future_result(
102
+ future,
103
+ "Could not generate sitemap",
104
+ )
105
+ )
106
+ sitemap_future.add_done_callback(lambda _: progress.advance(other_task))
107
+
108
+ for filepath in content_directory.rglob("*"):
109
+ if filepath.is_dir():
110
+ continue
111
+ if filepath.suffix == ".md":
112
+
113
+ def on_markdown_file_processed(future: concurrent.futures.Future):
114
+ progress.advance(markdown_task)
115
+ if exception := future.exception():
116
+ print(f"{filepath}: could not process file - {exception}")
117
+
118
+ markdown_future = executor.submit(
119
+ write_html_file,
120
+ filepath.relative_to(content_directory),
121
+ file_data_by_directory,
122
+ release,
123
+ jinja_env,
124
+ )
125
+ markdown_task_count += 1
126
+ progress.update(markdown_task, total=markdown_task_count)
127
+ markdown_future.add_done_callback(on_markdown_file_processed)
128
+ continue
129
+ other_future = executor.submit(
130
+ process_non_markdown_file,
131
+ filepath,
132
+ file_data_by_directory,
133
+ jinja_env,
134
+ )
135
+ non_markdown_task_count += 1
136
+ progress.update(other_task, total=non_markdown_task_count)
137
+
138
+ def on_non_markdown_file_processed(future: concurrent.futures.Future):
139
+ progress.advance(other_task)
140
+ if exception := future.exception():
141
+ print(f"{filepath}: could not process file - {exception}")
142
+
143
+ other_future.add_done_callback(on_non_markdown_file_processed)
144
+
145
+ print(
146
+ f"Blurring {markdown_task_count} Markdown files and {non_markdown_task_count} other files"
147
+ )
148
+
149
+ end = datetime.now()
150
+
151
+ difference = end - start
152
+ print(f"Built site in {difference.total_seconds()} seconds")
153
+
154
+
155
+ async def reload_local_plugins_and_build():
156
+ """Reloads a project's local modules and performs a dev build"""
157
+ cwd = str(Path.cwd())
158
+
159
+ for _, package_name, _ in pkgutil.iter_modules([cwd]):
160
+ try:
161
+ module = sys.modules[package_name]
162
+ except KeyError:
163
+ continue
164
+ importlib.reload(module)
165
+
166
+ await build(release=False)
167
+
168
+
169
+ @app.command()
170
+ def runserver():
171
+ """Starts HTTP server with live reloading."""
172
+ print_blurry_name()
173
+ print_plugin_table()
174
+
175
+ SETTINGS = get_settings()
176
+ os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "dev")
177
+
178
+ SETTINGS["RUNSERVER"] = True
179
+
180
+ event_loop = asyncio.get_event_loop()
181
+ event_loop.create_task(build(release=False))
182
+
183
+ jinja_env = get_jinja_env()
184
+
185
+ livereload_server = Server()
186
+ livereload_server.watch(
187
+ f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*.jinja",
188
+ lambda filepaths: handle_changed_jinja_files(filepaths, jinja_env),
189
+ )
190
+ livereload_server.watch(
191
+ f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*.md",
192
+ lambda filepaths: handle_changed_markdown_files(filepaths, jinja_env),
193
+ )
194
+ livereload_server.watch(
195
+ f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*",
196
+ lambda: event_loop.create_task(build(release=False)),
197
+ ignore=lambda filepath: any(
198
+ [
199
+ filepath.endswith(".md"),
200
+ filepath.endswith(".jinja"),
201
+ Path(filepath).is_dir(),
202
+ ]
203
+ ),
204
+ )
205
+ livereload_server.watch(
206
+ f"{SETTINGS['TEMPLATES_DIRECTORY_NAME']}/**/*",
207
+ lambda: rebuild_markdown_files(jinja_env),
208
+ ignore=lambda filepath: Path(filepath).is_dir(),
209
+ )
210
+ livereload_server.watch(
211
+ "./**/*.py",
212
+ lambda: event_loop.create_task(reload_local_plugins_and_build()),
213
+ )
214
+ livereload_server.watch(
215
+ "blurry.toml", lambda: event_loop.create_task(build(release=False))
216
+ )
217
+
218
+ for header_name, header_value in SETTINGS["DEV_HEADERS"].items():
219
+ livereload_server.setHeader(header_name, header_value)
220
+
221
+ livereload_server.serve(
222
+ host=SETTINGS["DEV_HOST"], port=SETTINGS["DEV_PORT"], root=get_build_directory()
223
+ )
224
+
225
+
226
+ def main():
227
+ app()
@@ -0,0 +1,3 @@
1
+ from blurry import main
2
+
3
+ main()
@@ -0,0 +1,20 @@
1
+ import asyncio
2
+ from functools import wraps
3
+
4
+ from typer import Typer
5
+
6
+
7
+ class AsyncTyper(Typer):
8
+ def async_command(self, *args, **kwargs):
9
+ def decorator(async_func):
10
+ # Convert async function to synchronous
11
+ @wraps(async_func)
12
+ def sync_func(*_args, **_kwargs):
13
+ return asyncio.run(async_func(*_args, **_kwargs))
14
+
15
+ # Register synchronous function
16
+ self.command(*args, **kwargs)(sync_func)
17
+
18
+ return async_func
19
+
20
+ return decorator
@@ -0,0 +1,37 @@
1
+ from rich.console import Console
2
+ from rich.table import Table
3
+
4
+ from blurry.plugins import discovered_html_plugins
5
+ from blurry.plugins import discovered_jinja_extensions
6
+ from blurry.plugins import discovered_jinja_filter_plugins
7
+ from blurry.plugins import discovered_markdown_plugins
8
+
9
+ console = Console()
10
+
11
+
12
+ def print_blurry_name():
13
+ console.print(
14
+ """
15
+ . .
16
+ |-.| . ..-..-.. .
17
+ `-''-'-'' ' '-|
18
+ `-'
19
+ """.strip()
20
+ )
21
+
22
+
23
+ def print_plugin_table():
24
+ plugin_table = Table(show_header=True)
25
+ plugin_table.add_column("Markdown Plugins")
26
+ plugin_table.add_column("HTML Plugins")
27
+ plugin_table.add_column("Jinja Plugins")
28
+ plugin_table.add_row(
29
+ "\n".join([p.name for p in discovered_markdown_plugins]),
30
+ "\n".join([p.name for p in discovered_html_plugins]),
31
+ "\n".join(
32
+ [p.name for p in discovered_jinja_filter_plugins]
33
+ + [p.name for p in discovered_jinja_extensions]
34
+ ),
35
+ )
36
+
37
+ console.print(plugin_table)
File without changes
@@ -0,0 +1,19 @@
1
+ import shutil
2
+
3
+ from rich import print
4
+
5
+ from blurry.settings import get_build_directory
6
+ from blurry.settings import update_settings
7
+
8
+
9
+ def clean_build_directory():
10
+ """Removes the build directory for a clean build."""
11
+ update_settings()
12
+ build_directory = get_build_directory()
13
+
14
+ try:
15
+ shutil.rmtree(build_directory)
16
+ except FileNotFoundError:
17
+ pass
18
+
19
+ print(f"Cleaned build directory: {build_directory} 🧹")
@@ -0,0 +1,135 @@
1
+ import tomllib
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+
5
+ from rich.prompt import Prompt
6
+
7
+ from blurry.settings import get_content_directory
8
+ from blurry.settings import get_templates_directory
9
+ from blurry.settings import update_settings
10
+
11
+
12
+ BLURRY_CONFIG_TEMPLATE = """
13
+ [blurry]
14
+ domain = "{domain}"
15
+ markdown_file_jinja_template_extension = ".jinja"
16
+
17
+ [blurry.schema_data.sourceOrganization]
18
+ name = "{project_name}"
19
+ url = "https://{domain}"
20
+ """.strip()
21
+
22
+ HOMEPAGE_MARKDOWN = """
23
+ +++
24
+ "@type" = "WebSite"
25
+ name = "Home"
26
+ abstract = "The homepage of {project_name}, built with Blurry."
27
+ datePublished = {date_published}
28
+ +++
29
+
30
+ # Welcome to your new Blurry site
31
+
32
+ To learn more about how to get started, check out Blurry's quick start docs:
33
+
34
+ <https://blurry-docs.netlify.app/getting-started/quick-start/>
35
+ """.strip()
36
+
37
+ BASE_TEMPLATE = """
38
+ <!doctype html>
39
+ <html lang="en">
40
+ <head>
41
+ <meta charset="utf-8">
42
+ <meta name="viewport" content="width=device-width, initial-scale=1">
43
+ <meta name="color-scheme" content="light dark">
44
+ <link
45
+ rel="stylesheet"
46
+ href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
47
+ >
48
+ <title>{% block title %}{% endblock %} | {{ sourceOrganization.name }}</title>
49
+ <meta name="description" content="{% block description %}{% endblock %}">
50
+ <link rel="canonical" href="{{ url }}">
51
+ {{ open_graph_tags|safe }}
52
+ {{ schema_type_tag|safe }}
53
+ </head>
54
+ <body>
55
+ <nav class="container">
56
+ <ul>
57
+ <li><strong>{{ sourceOrganization.name }}</strong></li>
58
+ </ul>
59
+ <ul>
60
+ <li><a href="/">Home</a></li>
61
+ </ul>
62
+ </nav>
63
+
64
+ <main class="container">
65
+ {% block body %}{% endblock %}
66
+ </main>
67
+ </body>
68
+ </html>
69
+ """
70
+
71
+
72
+ WEBSITE_TEMPLATE = """
73
+ {% extends "base.jinja" %}
74
+
75
+ {% block title %}{{ name }}{% endblock %}
76
+ {% block description %}{{ abstract }}{% endblock %}
77
+
78
+ {% block body %}
79
+ <article>
80
+ {{ body|safe }}
81
+ </article>
82
+ {% endblock %}
83
+ """
84
+
85
+
86
+ def initialize_new_project(name: str | None, domain: str | None):
87
+ update_settings()
88
+ blurry_config_file = Path("blurry.toml")
89
+ blurry_template_directory = get_templates_directory()
90
+ blurry_content_directory = get_content_directory()
91
+
92
+ if (
93
+ blurry_config_file.exists()
94
+ or blurry_template_directory.exists()
95
+ or blurry_content_directory.exists()
96
+ ):
97
+ print("Blurry project already initialized.")
98
+ return
99
+ name = name or Prompt.ask(
100
+ "What is the name of your company, project, or website?",
101
+ )
102
+ domain = domain or Prompt.ask("What is your website's domain?")
103
+
104
+ config_text = BLURRY_CONFIG_TEMPLATE.format(domain=domain, project_name=name)
105
+
106
+ try:
107
+ tomllib.loads(config_text)
108
+ except tomllib.TOMLDecodeError as e:
109
+ print(f"Error in configuration file: {e}.")
110
+ print("Please check your input and try again.")
111
+ exit(1)
112
+
113
+ blurry_config_file.write_text(config_text)
114
+
115
+ # Write template files
116
+ blurry_template_directory.mkdir(exist_ok=True)
117
+ base_template_file = blurry_template_directory / "base.jinja"
118
+ base_template_file.write_text(BASE_TEMPLATE)
119
+
120
+ website_template_file = blurry_template_directory / "WebSite.jinja"
121
+ website_template_file.write_text(WEBSITE_TEMPLATE)
122
+
123
+ # Write homepage Markdown file
124
+ date_published = datetime.now().strftime("%Y-%m-%d")
125
+ blurry_content_directory.mkdir(exist_ok=True)
126
+ homepage = blurry_content_directory / "index.md"
127
+ homepage.write_text(
128
+ HOMEPAGE_MARKDOWN.format(
129
+ date_published=date_published,
130
+ project_name=name,
131
+ )
132
+ )
133
+
134
+ print("Blurry project initialized!")
135
+ print("Run 'blurry runserver' to start the dev server.")
@@ -0,0 +1,9 @@
1
+ import importlib.metadata
2
+ from rich import print
3
+
4
+
5
+ def print_blurry_version():
6
+ """Prints the current Blurry version."""
7
+ version = importlib.metadata.version("blurry-cli")
8
+
9
+ print(f"v{version}")
@@ -0,0 +1,7 @@
1
+ from pathlib import Path
2
+
3
+ ENV_VAR_PREFIX = "BLURRY_"
4
+
5
+ SETTINGS_FILENAME = "blurry.toml"
6
+
7
+ CURR_DIR = Path.cwd()