You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .portfolio/architecture.md
+15Lines changed: 15 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -189,6 +189,21 @@ Features like Docker, GitHub Actions, and VS Code settings are modular add-ons:
189
189
-**Post-creation addition**: Features can be added to existing projects via `quickforge add`
190
190
-**Independent templates**: Each feature has its own template set
191
191
192
+
### 8. Context-Aware Escaping for Generated Code
193
+
194
+
The Jinja2 environment runs with autoescaping disabled — correct for emitting code and config rather than HTML, but it shifts the responsibility for escaping onto the templates. Since a project's description and author name are free text, a stray quote, backslash, or brace would otherwise produce a broken `pyproject.toml` or unparsable Python file.
195
+
196
+
I solved this with destination-specific filters in `generator.py` rather than blanket input sanitization:
197
+
198
+
-`toml_escape` for TOML basic strings
199
+
-`py_escape` for Python string and docstring literals
200
+
-`fstring_escape` for f-string contexts (also doubles `{`/`}` so braces aren't read as replacement fields)
201
+
-`github_slug` to reduce an author name to a URL-safe path segment
202
+
203
+
This keeps user input verbatim where it's displayed and escapes it precisely where it's embedded, so the generated `pyproject.toml` round-trips the exact description while still parsing as valid TOML.
204
+
205
+
A related concern is license metadata: PyPI trove classifiers use a controlled vocabulary that does not match SPDX identifiers (SPDX `Apache-2.0` is the classifier `Apache Software License`). `License.classifier` and `License.spdx_id` in `models.py` map each license to the correct value for both fields, and the non-SPDX `Proprietary` case is emitted as a `LicenseRef-` expression so the generated metadata is valid.
Copy file name to clipboardExpand all lines: .portfolio/qa.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,6 +41,9 @@ Project creation is a linear pipeline (`validate -> create dirs -> render templa
41
41
### Conditional template rendering
42
42
Templates are tagged by archetype and feature flags. `cli.py.j2` is rendered only for `--type cli`; the FastAPI router only for `--type api`; Docker, MkDocs, devcontainer files only when their flags are set. The generated tree contains only files the user actually asked for.
43
43
44
+
### Context-aware escaping in a code generator
45
+
Jinja2 autoescaping is built for HTML; for emitting TOML and Python it's the wrong default, so it's disabled — which means free-text fields (a project description, an author name) could otherwise inject a stray quote or brace and break the generated files. Rather than sanitizing input globally, I escape at the point of use with destination-specific filters in `src/quickforge/generator.py`: `toml_escape` for TOML strings, `py_escape` for Python literals and docstrings, and `fstring_escape` for f-string contexts (which additionally doubles braces so they aren't parsed as replacement fields). The result is that a description containing `"` and `{}` is preserved verbatim in `pyproject.toml` while the file still parses as valid TOML.
46
+
44
47
## Engineering Decisions
45
48
46
49
### tomlkit vs tomli for upgrades
@@ -81,6 +84,12 @@ One tool replaces three, with a single config block in `pyproject.toml` and subs
81
84
### Why basedpyright instead of mypy?
82
85
basedpyright (a fork of pyright) is significantly faster, stricter by default, and produces clearer error messages with better editor integration. For new projects starting fresh, the stricter defaults catch bugs the day they're written.
83
86
87
+
### What happens if my project description or author name contains quotes or braces?
88
+
It's handled. Because the generator emits code and config (not HTML), free-text fields are escaped per destination — TOML strings, Python literals/docstrings, and f-strings each have their own escaping rule — so a description like `He said "hi" {now}` lands intact in `pyproject.toml` and the generated Python still compiles.
89
+
90
+
### Which licenses generate a real LICENSE file?
91
+
All six offered (MIT, Apache-2.0, GPL-3.0-only, BSD-3-Clause, Unlicense, Proprietary) emit a complete `LICENSE` file, and the project's PyPI trove classifier and SPDX expression are set to the correct values for the chosen license — including the `LicenseRef-` form for the non-SPDX Proprietary case.
92
+
84
93
### Can I customize the generated templates?
85
94
Not at the moment — templates are bundled with the package. The intent is that the generated output is opinionated. If you need a different stack, fork the project or modify the generated files after creation.
0 commit comments