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.
- blurry_cli-0.22.0/LICENSE +21 -0
- blurry_cli-0.22.0/PKG-INFO +92 -0
- blurry_cli-0.22.0/README.md +51 -0
- blurry_cli-0.22.0/blurry/__init__.py +227 -0
- blurry_cli-0.22.0/blurry/__main__.py +3 -0
- blurry_cli-0.22.0/blurry/async_typer.py +20 -0
- blurry_cli-0.22.0/blurry/cli.py +37 -0
- blurry_cli-0.22.0/blurry/commands/__init__.py +0 -0
- blurry_cli-0.22.0/blurry/commands/clean.py +19 -0
- blurry_cli-0.22.0/blurry/commands/init.py +135 -0
- blurry_cli-0.22.0/blurry/commands/version.py +9 -0
- blurry_cli-0.22.0/blurry/constants.py +7 -0
- blurry_cli-0.22.0/blurry/file_processors/__init__.py +156 -0
- blurry_cli-0.22.0/blurry/gather_file_data_by_directory.py +60 -0
- blurry_cli-0.22.0/blurry/images.py +125 -0
- blurry_cli-0.22.0/blurry/markdown/__init__.py +227 -0
- blurry_cli-0.22.0/blurry/markdown/front_matter.py +56 -0
- blurry_cli-0.22.0/blurry/markdown/renderer_functions/__init__.py +0 -0
- blurry_cli-0.22.0/blurry/markdown/renderer_functions/render_video.py +34 -0
- blurry_cli-0.22.0/blurry/open_graph.py +57 -0
- blurry_cli-0.22.0/blurry/plugins/__init__.py +6 -0
- blurry_cli-0.22.0/blurry/plugins/html_plugins/__init__.py +0 -0
- blurry_cli-0.22.0/blurry/plugins/html_plugins/minify_html_plugin.py +37 -0
- blurry_cli-0.22.0/blurry/plugins/jinja_plugins/__init__.py +0 -0
- blurry_cli-0.22.0/blurry/plugins/jinja_plugins/blurry_image_extension.py +75 -0
- blurry_cli-0.22.0/blurry/plugins/jinja_plugins/filters.py +43 -0
- blurry_cli-0.22.0/blurry/plugins/markdown_plugins/__init__.py +0 -0
- blurry_cli-0.22.0/blurry/plugins/markdown_plugins/container_plugin.py +40 -0
- blurry_cli-0.22.0/blurry/plugins/markdown_plugins/punctuation_plugin.py +25 -0
- blurry_cli-0.22.0/blurry/plugins/markdown_plugins/python_code_plugin.py +49 -0
- blurry_cli-0.22.0/blurry/py.typed +0 -0
- blurry_cli-0.22.0/blurry/runserver_handlers.py +37 -0
- blurry_cli-0.22.0/blurry/schema_validation/__init__.py +38 -0
- blurry_cli-0.22.0/blurry/schema_validation/models.py +574 -0
- blurry_cli-0.22.0/blurry/settings.py +99 -0
- blurry_cli-0.22.0/blurry/sitemap.py +49 -0
- blurry_cli-0.22.0/blurry/templates.py +47 -0
- blurry_cli-0.22.0/blurry/types.py +36 -0
- blurry_cli-0.22.0/blurry/utils.py +124 -0
- 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
|
+
[](https://github.com/blurry-dev/blurry/actions/workflows/github-actions-nox.yml)
|
|
44
|
+
[](https://pypi.org/project/blurry-cli/)
|
|
45
|
+
[](https://pypi.org/project/blurry-cli/)
|
|
46
|
+
[](https://pypi.python.org/pypi/blurry-cli/)
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+

|
|
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
|
+
© 2020-present, John Franey
|
|
88
|
+
|
|
89
|
+
## Star History
|
|
90
|
+
|
|
91
|
+
[](https://www.star-history.com/#blurry-dev/blurry&type=date&legend=top-left)
|
|
92
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Blurry
|
|
2
|
+
|
|
3
|
+
[](https://github.com/blurry-dev/blurry/actions/workflows/github-actions-nox.yml)
|
|
4
|
+
[](https://pypi.org/project/blurry-cli/)
|
|
5
|
+
[](https://pypi.org/project/blurry-cli/)
|
|
6
|
+
[](https://pypi.python.org/pypi/blurry-cli/)
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+

|
|
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
|
+
© 2020-present, John Franey
|
|
48
|
+
|
|
49
|
+
## Star History
|
|
50
|
+
|
|
51
|
+
[](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,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.")
|