jamstack 1.0.0__tar.gz → 2.0.0rc1__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 (109) hide show
  1. jamstack-2.0.0rc1/.editorconfig +9 -0
  2. jamstack-2.0.0rc1/.github/workflows/ci.yml +59 -0
  3. jamstack-2.0.0rc1/.github/workflows/release.yml +42 -0
  4. jamstack-2.0.0rc1/.gitignore +134 -0
  5. jamstack-2.0.0rc1/.pre-commit-config.yaml +19 -0
  6. jamstack-2.0.0rc1/CHANGELOG.md +62 -0
  7. jamstack-2.0.0rc1/CONTRIBUTING.md +74 -0
  8. jamstack-2.0.0rc1/PKG-INFO +226 -0
  9. jamstack-2.0.0rc1/README.md +191 -0
  10. jamstack-2.0.0rc1/docs/assets/extra.css +196 -0
  11. jamstack-2.0.0rc1/docs/assets/favicon.png +0 -0
  12. jamstack-2.0.0rc1/docs/assets/logo.png +0 -0
  13. jamstack-2.0.0rc1/docs/assets/wordmark.png +0 -0
  14. jamstack-2.0.0rc1/docs/cli.md +86 -0
  15. jamstack-2.0.0rc1/docs/configuration.md +80 -0
  16. jamstack-2.0.0rc1/docs/content.md +121 -0
  17. jamstack-2.0.0rc1/docs/contributing.md +54 -0
  18. jamstack-2.0.0rc1/docs/deployment.md +104 -0
  19. jamstack-2.0.0rc1/docs/index.md +83 -0
  20. jamstack-2.0.0rc1/docs/migration.md +149 -0
  21. jamstack-2.0.0rc1/docs/themes.md +176 -0
  22. jamstack-2.0.0rc1/docs/tutorial.md +185 -0
  23. jamstack-2.0.0rc1/mkdocs.yml +61 -0
  24. jamstack-2.0.0rc1/pyproject.toml +79 -0
  25. jamstack-2.0.0rc1/src/jamstack/__about__.py +1 -0
  26. jamstack-2.0.0rc1/src/jamstack/__init__.py +3 -0
  27. jamstack-2.0.0rc1/src/jamstack/__main__.py +4 -0
  28. jamstack-2.0.0rc1/src/jamstack/builder.py +70 -0
  29. jamstack-2.0.0rc1/src/jamstack/cli.py +113 -0
  30. jamstack-2.0.0rc1/src/jamstack/config.py +60 -0
  31. jamstack-2.0.0rc1/src/jamstack/content.py +131 -0
  32. jamstack-2.0.0rc1/src/jamstack/feed.py +75 -0
  33. jamstack-2.0.0rc1/src/jamstack/render.py +12 -0
  34. jamstack-2.0.0rc1/src/jamstack/server.py +92 -0
  35. jamstack-2.0.0rc1/src/jamstack/theme.py +84 -0
  36. jamstack-2.0.0rc1/src/jamstack/themes/blog/scaffold/content/posts/hello-world.md +9 -0
  37. jamstack-2.0.0rc1/src/jamstack/themes/blog/static/style.css +186 -0
  38. jamstack-2.0.0rc1/src/jamstack/themes/blog/templates/404.html +15 -0
  39. jamstack-2.0.0rc1/src/jamstack/themes/blog/templates/base.html +22 -0
  40. jamstack-2.0.0rc1/src/jamstack/themes/blog/templates/index.html +21 -0
  41. jamstack-2.0.0rc1/src/jamstack/themes/blog/templates/page.html +3 -0
  42. jamstack-2.0.0rc1/src/jamstack/themes/blog/templates/post.html +16 -0
  43. jamstack-2.0.0rc1/src/jamstack/themes/blog/theme.toml +2 -0
  44. jamstack-2.0.0rc1/src/jamstack/themes/minimal/static/style.css +137 -0
  45. jamstack-2.0.0rc1/src/jamstack/themes/minimal/templates/404.html +11 -0
  46. jamstack-2.0.0rc1/src/jamstack/themes/minimal/templates/base.html +21 -0
  47. jamstack-2.0.0rc1/src/jamstack/themes/minimal/templates/index.html +17 -0
  48. jamstack-2.0.0rc1/src/jamstack/themes/minimal/templates/page.html +11 -0
  49. jamstack-2.0.0rc1/src/jamstack/themes/minimal/theme.toml +2 -0
  50. jamstack-2.0.0rc1/tests/test_404.py +24 -0
  51. jamstack-2.0.0rc1/tests/test_build.py +114 -0
  52. jamstack-2.0.0rc1/tests/test_cli.py +72 -0
  53. jamstack-2.0.0rc1/tests/test_config.py +45 -0
  54. jamstack-2.0.0rc1/tests/test_content.py +72 -0
  55. jamstack-2.0.0rc1/tests/test_feed.py +72 -0
  56. jamstack-2.0.0rc1/tests/test_package.py +5 -0
  57. jamstack-2.0.0rc1/tests/test_server.py +107 -0
  58. jamstack-2.0.0rc1/tests/test_theme.py +58 -0
  59. jamstack-1.0.0/MANIFEST.in +0 -2
  60. jamstack-1.0.0/PKG-INFO +0 -86
  61. jamstack-1.0.0/README.md +0 -61
  62. jamstack-1.0.0/jamstack/__init__.py +0 -2
  63. jamstack-1.0.0/jamstack/__main__.py +0 -62
  64. jamstack-1.0.0/jamstack/__pycache__/__init__.cpython-39.pyc +0 -0
  65. jamstack-1.0.0/jamstack/__pycache__/__main__.cpython-39.pyc +0 -0
  66. jamstack-1.0.0/jamstack/api/__pycache__/file.cpython-39.pyc +0 -0
  67. jamstack-1.0.0/jamstack/api/__pycache__/template.cpython-39.pyc +0 -0
  68. jamstack-1.0.0/jamstack/api/file.py +0 -135
  69. jamstack-1.0.0/jamstack/api/template.py +0 -50
  70. jamstack-1.0.0/jamstack/jamdo/__pycache__/jamdo.cpython-39.pyc +0 -0
  71. jamstack-1.0.0/jamstack/jamdo/jamdo.py +0 -72
  72. jamstack-1.0.0/jamstack/sites/html5up/massively/info.json +0 -8
  73. jamstack-1.0.0/jamstack/sites/html5up/massively/settings.py +0 -14
  74. jamstack-1.0.0/jamstack/sites/html5up/massively/static.py +0 -54
  75. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/index.html +0 -33
  76. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/index2.html +0 -33
  77. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/index3.html +0 -33
  78. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/sections/bottom_scripts.html +0 -8
  79. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/sections/footer.html +0 -48
  80. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/sections/head_content.html +0 -5
  81. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/sections/main.html +0 -17
  82. jamstack-1.0.0/jamstack/sites/html5up/massively/templates/sections/nav.html +0 -14
  83. jamstack-1.0.0/jamstack/sites/html5up/phantom/info.json +0 -74
  84. jamstack-1.0.0/jamstack/sites/html5up/phantom/posts.json +0 -76
  85. jamstack-1.0.0/jamstack/sites/html5up/phantom/settings.py +0 -11
  86. jamstack-1.0.0/jamstack/sites/html5up/phantom/static.py +0 -65
  87. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/elements.html +0 -397
  88. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/generic.html +0 -58
  89. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/index.html +0 -78
  90. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/post_template.html +0 -57
  91. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/sections/bottom_scripts.html +0 -5
  92. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/sections/footer.html +0 -34
  93. jamstack-1.0.0/jamstack/sites/html5up/phantom/templates/sections/head_content.html +0 -10
  94. jamstack-1.0.0/jamstack/sites/plain/info.json +0 -8
  95. jamstack-1.0.0/jamstack/sites/plain/settings.py +0 -7
  96. jamstack-1.0.0/jamstack/sites/plain/static.py +0 -47
  97. jamstack-1.0.0/jamstack/sites/plain/templates/index.html +0 -9
  98. jamstack-1.0.0/jamstack/sites/plain/templates/sections/body.html +0 -2
  99. jamstack-1.0.0/jamstack/sites/plain/templates/sections/head.html +0 -10
  100. jamstack-1.0.0/jamstack.egg-info/PKG-INFO +0 -86
  101. jamstack-1.0.0/jamstack.egg-info/SOURCES.txt +0 -50
  102. jamstack-1.0.0/jamstack.egg-info/dependency_links.txt +0 -1
  103. jamstack-1.0.0/jamstack.egg-info/entry_points.txt +0 -3
  104. jamstack-1.0.0/jamstack.egg-info/requires.txt +0 -11
  105. jamstack-1.0.0/jamstack.egg-info/top_level.txt +0 -1
  106. jamstack-1.0.0/requirements.txt +0 -11
  107. jamstack-1.0.0/setup.cfg +0 -7
  108. jamstack-1.0.0/setup.py +0 -49
  109. {jamstack-1.0.0 → jamstack-2.0.0rc1}/LICENSE +0 -0
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*.{md,py,txt}]
4
+ indent_style = space
5
+ indent_size = 4
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
@@ -0,0 +1,59 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [stable, "2.0"]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.13"
20
+ - run: pip install ruff
21
+ - run: ruff check .
22
+ - run: ruff format --check .
23
+
24
+ test:
25
+ runs-on: ubuntu-latest
26
+ strategy:
27
+ fail-fast: false
28
+ matrix:
29
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: actions/setup-python@v5
33
+ with:
34
+ python-version: ${{ matrix.python-version }}
35
+ - run: pip install -e ".[dev]"
36
+ - run: pytest --cov --cov-report=term-missing
37
+
38
+ commits:
39
+ if: github.event_name == 'pull_request'
40
+ runs-on: ubuntu-latest
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ with:
44
+ fetch-depth: 0
45
+ - uses: actions/setup-python@v5
46
+ with:
47
+ python-version: "3.13"
48
+ - run: pip install commitizen
49
+ - run: cz check --rev-range origin/${{ github.base_ref }}..HEAD
50
+
51
+ docs:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - uses: actions/setup-python@v5
56
+ with:
57
+ python-version: "3.13"
58
+ - run: pip install -e ".[dev]"
59
+ - run: zensical build --strict
@@ -0,0 +1,42 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.13"
18
+ - run: pip install -e ".[dev]" build
19
+ - run: ruff check .
20
+ - run: ruff format --check .
21
+ - run: pytest
22
+ - name: Check version matches tag
23
+ run: |
24
+ python -c "import jamstack, os; assert jamstack.__version__ == os.environ['GITHUB_REF_NAME'].lstrip('v'), jamstack.__version__"
25
+ - run: python -m build
26
+ - uses: actions/upload-artifact@v4
27
+ with:
28
+ name: dist
29
+ path: dist/
30
+
31
+ publish:
32
+ needs: build
33
+ runs-on: ubuntu-latest
34
+ environment: pypi
35
+ permissions:
36
+ id-token: write
37
+ steps:
38
+ - uses: actions/download-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,134 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ .idea/
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ /dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
90
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
91
+ # install all needed dependencies.
92
+ #Pipfile.lock
93
+
94
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95
+ __pypackages__/
96
+
97
+ # Celery stuff
98
+ celerybeat-schedule
99
+ celerybeat.pid
100
+
101
+ # SageMath parsed files
102
+ *.sage.py
103
+
104
+ # Environments
105
+ .env
106
+ .venv
107
+ env/
108
+ venv/
109
+ ENV/
110
+ env.bak/
111
+ venv.bak/
112
+
113
+ # Spyder project settings
114
+ .spyderproject
115
+ .spyproject
116
+
117
+ # Rope project settings
118
+ .ropeproject
119
+
120
+ # mkdocs documentation
121
+ /site
122
+
123
+ # mypy
124
+ .mypy_cache/
125
+ .dmypy.json
126
+ dmypy.json
127
+
128
+ # Pyre type checker
129
+ .pyre/
130
+
131
+ # Local tooling
132
+ .venv/
133
+ .ruff_cache/
134
+ site/
@@ -0,0 +1,19 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: check-toml
6
+ - id: check-yaml
7
+ - id: end-of-file-fixer
8
+ - id: trailing-whitespace
9
+ - repo: https://github.com/astral-sh/ruff-pre-commit
10
+ rev: v0.17.4
11
+ hooks:
12
+ - id: ruff
13
+ args: [--fix]
14
+ - id: ruff-format
15
+ - repo: https://github.com/commitizen-tools/commitizen
16
+ rev: v4.19.3
17
+ hooks:
18
+ - id: commitizen
19
+ stages: [commit-msg]
@@ -0,0 +1,62 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. Entries before
4
+ 2.0 were written by hand; later entries are generated from Conventional Commits.
5
+
6
+ ## 2.0.0rc1 (2026-11-07)
7
+
8
+ ### Breaking
9
+
10
+ - The 1.x engine is gone: projects built with `settings.py`, `static.py` and the
11
+ html5up themes must migrate to `site.toml` and `content/`. See `docs/migration.md`.
12
+
13
+ ### Feat
14
+
15
+ - **themes**: render a 404 page from the active theme
16
+ - **feed**: generate sitemap.xml and an Atom feed
17
+ - **serve**: rebuild the site when sources change
18
+ - **serve**: serve the built site over HTTP
19
+ - **themes**: add theme manifests and a reading-first blog theme
20
+ - **build**: render sites with the bundled minimal theme
21
+ - **content**: parse front matter and resolve page URLs
22
+ - **config**: load and validate site.toml
23
+
24
+ ### Fix
25
+
26
+ - **serve**: keep watching after a failed rebuild
27
+ - **serve**: flush status messages in redirected output
28
+
29
+ ## 1.1.0
30
+
31
+ * Bulk refactors and code documentation
32
+ * New commands for `static.py` files
33
+ * `--serve` for livewatch changes
34
+ * `--watch` for watch specific files and folders.
35
+ * `--port` especiffy serve port
36
+
37
+ ## 1.0.0
38
+
39
+ * Back to namespace method for write templates names
40
+ * Example: `html5up/massively` instead of just `massively`
41
+ * Template assets are not included in the library to minimize the size of the library, they were moved to
42
+ a [separate repository](https://github.com/jamstackpy/jamstack-templates).
43
+
44
+ ## 0.1.0
45
+
46
+ * Improve **Plain** template
47
+ * Add new templates (massively, phantom)
48
+ * When creating a project, instead of writing the template like this: **html5up/massively**, you can use the name
49
+ directly, i.e. **massively**
50
+ * The assets of each template are now included in the library
51
+
52
+ ## 0.0.4
53
+
54
+ * Add README.md info
55
+
56
+ ## 0.0.3
57
+
58
+ * Add plain template
59
+
60
+ ## 0.0.1
61
+
62
+ * Initial release
@@ -0,0 +1,74 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+
5
+ ```bash
6
+ git clone git@github.com:jamstackpy/jamstack.git
7
+ cd jamstack
8
+ git switch 2.0
9
+ python -m venv .venv
10
+ . .venv/bin/activate
11
+ pip install -e ".[dev]"
12
+ pre-commit install --hook-type pre-commit --hook-type commit-msg
13
+ ```
14
+
15
+ Run the checks before opening a pull request:
16
+
17
+ ```bash
18
+ ruff check .
19
+ ruff format --check .
20
+ pytest
21
+ ```
22
+
23
+ ## Commits
24
+
25
+ Commits follow [Conventional Commits](https://www.conventionalcommits.org/) in
26
+ English:
27
+
28
+ ```
29
+ feat(cli): add a --drafts flag to build
30
+ fix(content): reject dates without a year
31
+ docs: explain local theme overrides
32
+ ```
33
+
34
+ One commit is one reviewable intention. Do not mix unrelated changes under a
35
+ message that only describes one of them, and do not leave intermediate commits
36
+ that fail the tests. Mechanical changes (renames, formatting, dependency
37
+ bumps) go in their own commit.
38
+
39
+ Write a body when the change needs context: bullet points, in English,
40
+ explaining what and why. `commitizen` validates the message through the
41
+ `commit-msg` hook installed above.
42
+
43
+ ## Pull requests
44
+
45
+ - Target the `2.0` branch.
46
+ - Include a test for new behaviour; bug fixes should include the case that
47
+ reproduces the bug.
48
+ - Keep user-facing strings and documentation in English.
49
+ - Do not rewrite pushed history.
50
+
51
+ ## Releasing
52
+
53
+ Publishing runs on tags through GitHub Actions and PyPI trusted publishing,
54
+ with no API tokens stored anywhere.
55
+
56
+ One-time setup:
57
+
58
+ 1. On PyPI, open the `jamstack` project → Publishing → add a GitHub publisher
59
+ with owner `jamstackpy`, repository `jamstack`, workflow `release.yml`, and
60
+ environment `pypi`.
61
+ 2. On GitHub, create an environment named `pypi` in the repository settings.
62
+
63
+ For the first 2.0 release, the version in `src/jamstack/__about__.py` is
64
+ `2.0.0.dev0`. Set it to `2.0.0`, regenerate the changelog, and tag:
65
+
66
+ ```bash
67
+ cz changelog
68
+ git commit -am "chore(release): 2.0.0"
69
+ git tag v2.0.0
70
+ git push origin 2.0 v2.0.0
71
+ ```
72
+
73
+ Later releases use `cz bump`, which updates the version, rewrites
74
+ `CHANGELOG.md`, commits, and creates the tag in one step.
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.5
2
+ Name: jamstack
3
+ Version: 2.0.0rc1
4
+ Summary: Static site generator built on Python and Jinja
5
+ Project-URL: Homepage, https://github.com/jamstackpy/jamstack
6
+ Project-URL: Source, https://github.com/jamstackpy/jamstack
7
+ Project-URL: Issues, https://github.com/jamstackpy/jamstack/issues
8
+ Project-URL: Changelog, https://github.com/jamstackpy/jamstack/blob/stable/CHANGELOG.md
9
+ Author-email: Abdur-Rahmaan Janhangeer <arj.python@gmail.com>, Pedro Torcatt <pedrotorcattsoto@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: jinja,python,ssg,static site generator
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: click>=8.1
25
+ Requires-Dist: jinja2>=3.1
26
+ Requires-Dist: markdown-it-py>=3.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: commitizen>=4.0; extra == 'dev'
29
+ Requires-Dist: pre-commit>=3.7; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.17; extra == 'dev'
33
+ Requires-Dist: zensical>=0.1; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ <div align="center">
37
+ <img alt="Jamstack logo" src="https://i.imgur.com/sXUAdYJ.png" height="217" />
38
+
39
+ <p>A small static site generator built on Python and Jinja.</p>
40
+ </div>
41
+
42
+ [![CI](https://github.com/jamstackpy/jamstack/actions/workflows/ci.yml/badge.svg?branch=2.0)](https://github.com/jamstackpy/jamstack/actions/workflows/ci.yml)
43
+ ![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)
44
+
45
+ > **2.0 is under development.** This branch contains the rewrite and is not
46
+ > published on PyPI yet. The `stable` branch holds the current 1.x release.
47
+
48
+ ## Documentation
49
+
50
+ The full documentation lives in [`docs/`](docs/) and is built with Zensical.
51
+ Preview it locally with `zensical serve`; it is published at
52
+ <https://jamstackpy.github.io/>.
53
+
54
+ Jamstack turns a folder of Markdown and Jinja templates into a static site.
55
+ There is no database, no runtime, and no network access during a build: you
56
+ write content, run one command, and deploy the generated `dist/` folder
57
+ anywhere.
58
+
59
+ ## Features
60
+
61
+ - Markdown pages with TOML front matter, or raw Jinja templates when you need
62
+ full control.
63
+ - Themes as plain folders: bundled ones, your own, or a per-project override.
64
+ - A development server that rebuilds on change and keeps the last good build
65
+ when something fails.
66
+ - `sitemap.xml`, an Atom feed, and a themed `404.html` generated for you.
67
+ - Deterministic output: the same sources always produce the same bytes.
68
+ - Three runtime dependencies: `click`, `jinja2`, `markdown-it-py`.
69
+
70
+ ## Installation
71
+
72
+ Until 2.0 is published:
73
+
74
+ ```bash
75
+ python -m pip install "jamstack @ git+https://github.com/jamstackpy/jamstack@2.0"
76
+ ```
77
+
78
+ After the release:
79
+
80
+ ```bash
81
+ python -m pip install jamstack
82
+ ```
83
+
84
+ Python 3.11 or newer is required.
85
+
86
+ ## Quick start
87
+
88
+ ```bash
89
+ jamstack init my-site --theme blog
90
+ cd my-site
91
+ jamstack build
92
+ jamstack serve
93
+ ```
94
+
95
+ `jamstack init` scaffolds `site.toml`, a starter page, and the files the chosen
96
+ theme needs. `jamstack build` writes the site to `dist/`. `jamstack serve`
97
+ serves it at `http://127.0.0.1:8000` and rebuilds when a source changes.
98
+
99
+ ## Project layout
100
+
101
+ ```
102
+ my-site/
103
+ site.toml # site configuration
104
+ content/ # pages (.md or .html)
105
+ static/ # files copied as-is to the output root
106
+ templates/ # optional overrides of the theme's templates
107
+ themes/ # optional local themes
108
+ dist/ # build output (do not edit)
109
+ ```
110
+
111
+ ## Writing content
112
+
113
+ A page is a file under `content/`. Its path becomes its URL:
114
+
115
+ | Source | URL | Output |
116
+ | --- | --- | --- |
117
+ | `content/index.md` | `/` | `dist/index.html` |
118
+ | `content/about.md` | `/about/` | `dist/about/index.html` |
119
+ | `content/posts/hello.md` | `/posts/hello/` | `dist/posts/hello/index.html` |
120
+
121
+ Markdown files may start with TOML front matter delimited by `+++`:
122
+
123
+ ```markdown
124
+ +++
125
+ title = "Hello, world"
126
+ date = 2026-11-06
127
+ summary = "The first post."
128
+ +++
129
+
130
+ This is **Markdown**.
131
+ ```
132
+
133
+ Recognised keys:
134
+
135
+ | Key | Type | Meaning |
136
+ | --- | --- | --- |
137
+ | `title` | string | Page title. Defaults to the file name. |
138
+ | `date` | ISO date | Publication date. Feeds and listings order by it. |
139
+ | `template` | string | Template to render the page with. |
140
+ | `draft` | boolean | Excluded from builds unless `--drafts` is passed. |
141
+ | `summary` | string | Short description used by feeds and the blog theme. |
142
+
143
+ Any other key is available to templates as `page.meta`. Files ending in
144
+ `.html` are rendered as Jinja templates instead of Markdown, which makes it
145
+ easy to build landing pages with custom markup.
146
+
147
+ ## Configuration
148
+
149
+ ```toml
150
+ [site]
151
+ title = "My Site" # required
152
+ description = ""
153
+ author = ""
154
+ base_url = "https://example.com" # required for sitemap.xml and feed.xml
155
+ language = "en"
156
+
157
+ [build]
158
+ output = "dist"
159
+
160
+ [theme]
161
+ name = "blog"
162
+ ```
163
+
164
+ `base_url` must be the public, absolute URL of the site. When it is set,
165
+ Jamstack writes `sitemap.xml` with every page and `feed.xml` with every dated
166
+ page. When it is empty, both files are skipped rather than emitting relative
167
+ URLs.
168
+
169
+ ## Themes
170
+
171
+ List the available themes with:
172
+
173
+ ```bash
174
+ jamstack themes
175
+ ```
176
+
177
+ Two themes ship with the package: `minimal` for small sites and `blog` for
178
+ dated posts. A project can override a bundled theme by creating
179
+ `themes/<name>/` inside the project; local themes take precedence.
180
+
181
+ Themes are plain folders, so a custom theme is just a directory with a
182
+ `theme.toml`, `templates/`, and optionally `static/` and `scaffold/`. See
183
+ [docs/themes.md](docs/themes.md) for the full specification.
184
+
185
+ ## Command reference
186
+
187
+ | Command | Description |
188
+ | --- | --- |
189
+ | `jamstack init [PATH] [--theme NAME] [--force]` | Create a new site. |
190
+ | `jamstack build [--clean] [--drafts]` | Build the site into `dist/`. |
191
+ | `jamstack serve [--port PORT] [--drafts]` | Build, serve, and rebuild on change. |
192
+ | `jamstack themes` | List available themes. |
193
+ | `jamstack --version` | Print the installed version. |
194
+
195
+ `python -m jamstack` behaves like the `jamstack` command.
196
+
197
+ ## Migrating from 1.x
198
+
199
+ The 1.x engine (`settings.py`, `posts.json`, `static.py`, html5up themes) is
200
+ replaced entirely in 2.0. See [docs/migration.md](docs/migration.md) for a
201
+ step-by-step conversion guide, or keep using 1.x with:
202
+
203
+ ```bash
204
+ python -m pip install "jamstack<2"
205
+ ```
206
+
207
+ ## Development
208
+
209
+ ```bash
210
+ git clone git@github.com:jamstackpy/jamstack.git
211
+ cd jamstack
212
+ git switch 2.0
213
+ python -m venv .venv
214
+ . .venv/bin/activate
215
+ pip install -e ".[dev]"
216
+ pre-commit install --hook-type pre-commit --hook-type commit-msg
217
+ pytest
218
+ ```
219
+
220
+ Commits follow [Conventional Commits](https://www.conventionalcommits.org/);
221
+ see [CONTRIBUTING.md](CONTRIBUTING.md).
222
+
223
+ ## License
224
+
225
+ MIT. The 1.x html5up templates were distributed under CC BY 3.0 and are no
226
+ longer part of this branch.