diff --git a/docs/how-to/convert-html.md b/docs/how-to/convert-html.md new file mode 100644 index 0000000..ee6a2a4 --- /dev/null +++ b/docs/how-to/convert-html.md @@ -0,0 +1,143 @@ +# Convert HTML to htpy code + +Maybe you already have a bunch of HTML, or templates that you would like to migrate to htpy. We got you covered. This page describes how you can convert existing HTML to htpy code. + +The utility command `html2htpy` ships with `htpy`, and can be used to transform existing HTML into Python code (htpy!). + +Lets say you have an existing HTML file: + +```html title="index.html" + + + + + + htpy Recipes + + + + +
+

+ Recipe of the Day: Spaghetti Carbonara +

+

This classic Italian dish is quick and easy to make.

+
+ + + + +``` + +Now, if you run the command, it outputs the corresponding Python code (htpy). + +``` +$ html2htpy index.html +``` + +```py +from htpy import body, div, h1, h2, head, html, meta, p, span, title + +html(lang="en")[ + head[ + meta(charset="UTF-8"), + meta(name="viewport", content="width=device-width, initial-scale=1.0"), + title["htpy Recipes"], + ], + body[ + div("#header")[ + h1["Welcome to the cooking site"], p["Your go-to place for delicious recipes!"] + ], + div("#recipe-of-the-day.section")[ + h2["Recipe of the Day: ", span(".highlight")["Spaghetti Carbonara"]], + p["This classic Italian dish is quick and easy to make."], + ], + div("#footer")[p["© 2024 My Cooking Site. All rights reserved."]], + ], +] +``` + +## Convert HTML snippets from the clipboard + +This can be combined with other workflows in the way that you find most suitable. +For example, you might pipe from your clipboard to htpy, and optionally direct the output to a file. + +### Linux + +``` +xclip -o -selection clipboard | html2htpy > output.py +``` + +### Mac + +``` +pbpaste | html2htpy > output.py +``` + +### Windows + +``` +powershell Get-Clipboard | html2htpy > output.py +``` + +## Converting Django/Jinja templates + +`html2htpy` will convert Django/Jinja-style template variables to f-strings: + +``` html title="input" +
hi {{ name }}!
+``` + +``` py title="html2htpy output" + +from htpy import div + +div[f"hi { name }!"] + +``` + +### Limitations + +Other typical template syntax, such as loops `{% for x in y %}`, can not be transformed this way, +so you will often have to clean up a bit after `html2htpy` is done with its thing. + +See the example below: + +```html title="jinja.html" + +

{{ heading }}

+

Welcome to our cooking site, {{ user.name }}!

+ +

Recipe of the Day: {{ recipe.name }}

+

{{ recipe.description }}

+ +

Instructions:

+
    + {% for step in recipe.steps %} +
  1. {{ step }}
  2. + {% endfor %} +
+ +``` + +```py title="$ html2htpy jinja.html" +from htpy import body, h1, h2, h3, li, ol, p + +body[ + h1[f"{ heading }"], + p[f"Welcome to our cooking site, { user.name }!"], + h2[f"Recipe of the Day: { recipe.name }"], + p[f"{ recipe.description }"], + h3["Instructions:"], + ol[" {% for step in recipe.steps %}", li[f"{ step }"], " {% endfor %}"], +] +``` + +## VSCode Extension + +If you are using VSCode, you can install the [html2htpy](https://marketplace.visualstudio.com/items?itemName=dunderrrrrr.html2htpy) extension to quickly convert HTML to htpy code. diff --git a/docs/html2htpy.md b/docs/html2htpy.md deleted file mode 100644 index ce54722..0000000 --- a/docs/html2htpy.md +++ /dev/null @@ -1,205 +0,0 @@ -# Convert HTML to htpy Code - -Maybe you already have a bunch of HTML, or templates that you would like to migrate to htpy. -We got you covered. The utility command `html2htpy` ships with `htpy`, and can be used to transform existing -html into Python code (htpy!). - -``` -$ html2htpy -h -usage: html2htpy [-h] [-f {auto,ruff,black,none}] [-i {yes,h,no}] [--no-shorthand] [input] - -positional arguments: - input input HTML from file or stdin - -options: - -h, --help show this help message and exit - -f {auto,ruff,black,none}, --format {auto,ruff,black,none} - Select one of the following formatting options: auto, ruff, black or none - -i {yes,h,no}, --imports {yes,h,no} - Output mode for imports of found htpy elements - --no-shorthand Use explicit `id` and `class_` kwargs instead of the shorthand #id.class syntax -``` - -Lets say you have an existing HTML file: - -```html title="index.html" - - - - - - htpy Recipes - - - - -
-

- Recipe of the Day: Spaghetti Carbonara -

-

This classic Italian dish is quick and easy to make.

-
- - - - -``` - -Now, if you run the command, it outputs the corresponding Python code (htpy). - -``` -$ html2htpy index.html -``` - -```py -from htpy import body, div, h1, h2, head, html, meta, p, span, title - -html(lang="en")[ - head[ - meta(charset="UTF-8"), - meta(name="viewport", content="width=device-width, initial-scale=1.0"), - title["htpy Recipes"], - ], - body[ - div("#header")[ - h1["Welcome to the cooking site"], p["Your go-to place for delicious recipes!"] - ], - div("#recipe-of-the-day.section")[ - h2["Recipe of the Day: ", span(".highlight")["Spaghetti Carbonara"]], - p["This classic Italian dish is quick and easy to make."], - ], - div("#footer")[p["© 2024 My Cooking Site. All rights reserved."]], - ], -] -``` - -## Piping Input/Stdin Stream - -You can also pipe input to htpy, for example `cat demo.html | html2htpy`. - -This can be combined with other workflows in the way that you find most suitable. -For example, you might pipe from your clipboard to htpy, and optionally direct the output to a file. - -#### Linux - -``` -xclip -o -selection clipboard | html2htpy > output.py -``` - -#### Mac - -``` -pbpaste | html2htpy > output.py -``` - -#### Windows - -``` -powershell Get-Clipboard | html2htpy > output.py -``` - -## Formatting the Output - -`html2htpy` can format the output Python code using `black` or `ruff`. -Select the preferred formatter with the `-f`/`--format` flag. Options are `auto`, `ruff`, `black` and `none`. - -By default, the selection will be `auto`, formatting if it finds a formatter on path, preferring `black` if it's available. -If no formatters are available on path, the output will not be formatted. - -## Import Options - -You have a couple of options regarding imports with the `-i`/`--imports` flag. -Options are `yes` (default), `h`, `no`. - -#### Module import of htpy: `--imports=h` - -Some people prefer to `import htpy as h` instead of importing individual elements from htpy. -If this is you, you can use the `--imports=h` option to get corresponding output when using `html2htpy`. - -```py title="$ html2htpy --imports=h example.html" -import htpy as h - -h.section("#main-section.hero.is-link")[ - h.p(".subtitle.is-3.is-spaced")["Welcome"] -] -``` - -## Explicit ID and Class Kwargs - -If you prefer the explicit `id="id", class_="class"` kwargs syntax over the default htpy shorthand `#id.class` syntax, you can get it by passing the `--no-shorthand` flag. - -```html title="example.html" - -``` - -#### Default Shorthand Yield `#id.class` - -```py title="$ html2htpy example.html" -from htpy import p, section - -section("#main-section.hero.is-link")[ - p(".subtitle.is-3.is-spaced")["Welcome"] -] -``` - -#### No Shorthand Yields Kwargs `id`, `class_` - -```py title="$ html2htpy --no-shorthand example.html" -from htpy import p, section - -section(id="main-section", class_="hero is-link")[ - p(class_="subtitle is-3 is-spaced")["Welcome"] -] -``` - -## Template Interpolation to f-strings - -`html2htpy` will try to convert template variables to pythonic f-strings: - -`template {{ variables }}` -> `f"template { variables }"` - -Note that other typical template syntax, such as loops `{% for x in y %}`, can not be transformed this way, -so you will often have to clean up a bit after `html2htpy` is done with its thing. - -See the example below: - -```html title="jinja.html" - -

{{ heading }}

-

Welcome to our cooking site, {{ user.name }}!

- -

Recipe of the Day: {{ recipe.name }}

-

{{ recipe.description }}

- -

Instructions:

-
    - {% for step in recipe.steps %} -
  1. {{ step }}
  2. - {% endfor %} -
- -``` - -```py title="$ html2htpy jinja.html" -from htpy import body, h1, h2, h3, li, ol, p - -body[ - h1[f"{ heading }"], - p[f"Welcome to our cooking site, { user.name }!"], - h2[f"Recipe of the Day: { recipe.name }"], - p[f"{ recipe.description }"], - h3["Instructions:"], - ol[" {% for step in recipe.steps %}", li[f"{ step }"], " {% endfor %}"], -] -``` - -## VSCode Extension - -If you are using VSCode, you can install the [html2htpy](https://marketplace.visualstudio.com/items?itemName=dunderrrrrr.html2htpy) extension to quickly convert HTML to htpy code. \ No newline at end of file diff --git a/docs/reference/html2htpy.md b/docs/reference/html2htpy.md new file mode 100644 index 0000000..0918912 --- /dev/null +++ b/docs/reference/html2htpy.md @@ -0,0 +1,107 @@ + +# The html2htpy CLI tool + +The `html2htpy` command converts HTML code to htpy Python code. + +Looking to convert existing HTML? See the [Convert HTML to htpy guide](../how-to/convert-html.md). + +``` +usage: html2htpy [-h] [-f {auto,ruff,black,none}] [-i {yes,h,no}] [--no-shorthand] [input] + +positional arguments: + input Input HTML file, e.g. home.html. Optional. If not specified, html2htpy will read from stdin. + +options: + -h, --help show this help message and exit + -f, --format {auto,ruff,black,none} + + Format the output code with a code formatter. + + auto (default): + - If black is installed (exists on PATH), use `black` for formatting. + - If ruff is installed (exists on PATH): Use `ruff format` for formatting. + - If neither black or ruff is installed, do not perform any formatting. + + black: + Use the black formatter (https://black.readthedocs.io/en/stable/). + + ruff: + Use the ruff formatter (https://docs.astral.sh/ruff/formatter/). + + none: + Do not format the output code at all. + -i, --imports {yes,h,no} + + Specify formatting for imports. + + yes (default): + Add `from htpy import div, span` for all found elements. + h: + Add a single `import htpy as h`. + Reference elements with `h.div`, `h.span`. + no: + Do not add imports. + --no-shorthand Use explicit `id` and `class_` kwargs instead of the shorthand #id.class syntax. +``` + +## Import Options + +You have a couple of options regarding imports with the `-i`/`--imports` flag. +Options are `yes` (default), `h`, `no`. + +#### Module import of htpy: `--imports=h` + +This mode will use `import htpy as h` instead of importing individual elements from htpy. + +```py title="$ html2htpy --imports=h example.html" +import htpy as h + +h.section("#main-section.hero.is-link")[ + h.p(".subtitle.is-3.is-spaced")["Welcome"] +] +``` + +## id/classes conversion + +html2htpy supports multiple modes to handle id/class conversions: + +```html title="example.html" + +``` + +### Shorthand +By default, id/classes will be converted to the shorthand notation: + +```py title="$ html2htpy example.html" +from htpy import p, section + +section("#main-section.hero.is-link")[ + p(".subtitle.is-3.is-spaced")["Welcome"] +] +``` + +### Explicit id/class attributes + +Use the `--no-shorthand` flag to get explicit `id` and `class_` asttributes: + +```py title="$ html2htpy --no-shorthand example.html" +from htpy import p, section + +section(id="main-section", class_="hero is-link")[ + p(class_="subtitle is-3 is-spaced")["Welcome"] +] +``` + +## Django/Jinja variable conversion + +`html2htpy` will convert Django/Jinja-style template variables to f-strings: + +``` +$ echo '
hi {{ name }}!
' | html2htpy + +from htpy import div + +div[f"hi { name }!"] +``` diff --git a/mkdocs.yml b/mkdocs.yml index ee0c708..40140b2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,12 +28,15 @@ nav: - how-to/django.md - how-to/async.md - how-to/starlette.md + - how-to/convert-html.md - common-patterns.md - static-typing.md - streaming.md - - html2htpy.md - faq.md - references.md + - Reference: + - reference/html2htpy.md + markdown_extensions: - admonition - pymdownx.highlight: @@ -59,3 +62,4 @@ plugins: async.md: how-to/async.md django.md: how-to/django.md starlette.md: how-to/starlette.md + html2htpy.md: reference/html2htpy.md diff --git a/src/htpy/html2htpy.py b/src/htpy/html2htpy.py index d2cd5c6..fb28746 100644 --- a/src/htpy/html2htpy.py +++ b/src/htpy/html2htpy.py @@ -6,6 +6,7 @@ import shutil import subprocess import sys +import textwrap import typing as t from abc import ABC, abstractmethod from html.parser import HTMLParser @@ -432,25 +433,56 @@ def _is_command_available(command: str) -> bool: def main() -> None: - parser = argparse.ArgumentParser(prog="html2htpy") + parser = argparse.ArgumentParser( + prog="html2htpy", + formatter_class=argparse.RawTextHelpFormatter, + ) parser.add_argument( "-f", "--format", choices=["auto", "ruff", "black", "none"], default="auto", - help="Select one of the following formatting options: auto, ruff, black or none", + help=textwrap.dedent( + """ + Format the output code with a code formatter. + + auto (default): + - If black is installed (exists on PATH), use `black` for formatting. + - If ruff is installed (exists on PATH): Use `ruff format` for formatting. + - If neither black or ruff is installed, do not perform any formatting. + + black: + Use the black formatter (https://black.readthedocs.io/en/stable/). + + ruff: + Use the ruff formatter (https://docs.astral.sh/ruff/formatter/). + + none: + Do not format the output code at all. + """, + ), ) parser.add_argument( "-i", "--imports", choices=["yes", "h", "no"], - help="Output mode for imports of found htpy elements", + help=textwrap.dedent(""" + Specify formatting for imports. + + yes (default): + Add `from htpy import div, span` for all found elements. + h: + Add a single `import htpy as h`. + Reference elements with `h.div`, `h.span`. + no: + Do not add imports. + """), default="yes", ) parser.add_argument( "--no-shorthand", - help="Use explicit `id` and `class_` kwargs instead of the shorthand #id.class syntax", + help="Use explicit `id` and `class_` kwargs instead of the shorthand #id.class syntax.", action="store_true", ) parser.add_argument( @@ -458,7 +490,10 @@ def main() -> None: type=argparse.FileType("r"), nargs="?", default=sys.stdin, - help="input HTML from file or stdin", + help=( + "Input HTML file, e.g. home.html. " + "Optional. If not specified, html2htpy will read from stdin." + ), ) args = parser.parse_args()