htvl-lang 0.2.0__py3-none-any.whl

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.
htvl/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ """HTVL - a tiny HTML transpiler.
2
+
3
+ Public API::
4
+
5
+ from htvl import compile_source, compile_file, compile_project
6
+
7
+ html = compile_source("$(h 1 '' '' 'Hello' '')")
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from .emitter import Emitter, emit
13
+ from .errors import (
14
+ HtvlArityError,
15
+ HtvlError,
16
+ HtvlSyntaxError,
17
+ HtvlUnknownTagError,
18
+ HtvlUnknownVariableError,
19
+ )
20
+ from .parser import parse
21
+ from .project import (
22
+ CompileResult,
23
+ compile_file,
24
+ compile_project,
25
+ compile_source,
26
+ )
27
+
28
+ __version__ = "0.2.0"
29
+
30
+ __all__ = [
31
+ "__version__",
32
+ "compile_source",
33
+ "compile_file",
34
+ "compile_project",
35
+ "CompileResult",
36
+ "parse",
37
+ "emit",
38
+ "Emitter",
39
+ "HtvlError",
40
+ "HtvlSyntaxError",
41
+ "HtvlUnknownTagError",
42
+ "HtvlUnknownVariableError",
43
+ "HtvlArityError",
44
+ ]
htvl/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Entry point for ``python -m htvl``."""
2
+
3
+ import sys
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ sys.exit(main())
htvl/cli.py ADDED
@@ -0,0 +1,216 @@
1
+ """Command line interface: ``htvl compile``, ``htvl check``, ``htvl new``..."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import List, Optional
9
+
10
+ from . import __version__
11
+ from .elements import ELEMENTS
12
+ from .errors import HtvlError
13
+ from .project import compile_project, compile_source
14
+ from .templates import load_templates
15
+ from .variables import VARIABLES
16
+ from .watch import watch_project
17
+
18
+
19
+ def _build_parser() -> argparse.ArgumentParser:
20
+ parser = argparse.ArgumentParser(
21
+ prog="htvl",
22
+ description=(
23
+ "HTVL - a tiny HTML transpiler. Write short tag calls like "
24
+ "$(h 1 '' '' 'Hello' '') in .htvl files and compile them to HTML."
25
+ ),
26
+ )
27
+ parser.add_argument(
28
+ "--version", action="version", version=f"htvl {__version__}"
29
+ )
30
+ sub = parser.add_subparsers(dest="command", required=True)
31
+
32
+ compile_cmd = sub.add_parser(
33
+ "compile", help="compile every .htvl/.cal file in a directory"
34
+ )
35
+ compile_cmd.add_argument(
36
+ "path",
37
+ nargs="?",
38
+ default=".",
39
+ help="project directory (default: current directory)",
40
+ )
41
+ compile_cmd.add_argument(
42
+ "-o",
43
+ "--out",
44
+ default="public",
45
+ help="output directory for generated HTML (default: public)",
46
+ )
47
+ compile_cmd.add_argument(
48
+ "--modern",
49
+ action="store_true",
50
+ help=(
51
+ "emit clean HTML: omit empty attributes, escape text and fix "
52
+ "legacy cosmetic quirks (see docs/grammar.md)"
53
+ ),
54
+ )
55
+ compile_cmd.add_argument(
56
+ "-q", "--quiet", action="store_true", help="only report errors"
57
+ )
58
+ compile_cmd.add_argument(
59
+ "-w",
60
+ "--watch",
61
+ action="store_true",
62
+ help="keep running and recompile on every change (Ctrl-C to stop)",
63
+ )
64
+
65
+ check_cmd = sub.add_parser(
66
+ "check", help="compile a single file and write the HTML to stdout"
67
+ )
68
+ check_cmd.add_argument("files", nargs="+", help=".htvl files to compile")
69
+ check_cmd.add_argument(
70
+ "--modern",
71
+ action="store_true",
72
+ help="emit clean HTML (see docs/grammar.md)",
73
+ )
74
+
75
+ new_cmd = sub.add_parser("new", help="scaffold a new HTVL project")
76
+ new_cmd.add_argument("name", help="directory name for the new project")
77
+
78
+ sub.add_parser("list", help="list every tag and built-in variable")
79
+ return parser
80
+
81
+
82
+ def _cmd_compile(args: argparse.Namespace) -> int:
83
+ root = Path(args.path).resolve()
84
+ if not root.is_dir():
85
+ print(f"htvl: error: '{args.path}' is not a directory", file=sys.stderr)
86
+ return 1
87
+ if args.watch:
88
+ return _cmd_watch(args)
89
+ result = compile_project(root, out_dir=args.out, modern=args.modern)
90
+ if not args.quiet:
91
+ for output in result.outputs:
92
+ print(f"htvl: compiled {output.relative_to(root)}")
93
+ if result.copied_assets:
94
+ print(f"htvl: copied assets -> {result.copied_assets.relative_to(root)}")
95
+ if args.modern:
96
+ print("htvl: modern mode: clean, escaped HTML")
97
+ print(f"htvl: {len(result.outputs)} file(s) compiled")
98
+ return 0
99
+
100
+
101
+ def _cmd_watch(args: argparse.Namespace) -> int:
102
+ root = Path(args.path).resolve()
103
+
104
+ def say(line: str) -> None:
105
+ print(f"htvl: {line}")
106
+
107
+ try:
108
+ watch_project(root, out_dir=args.out, modern=args.modern, printer=say)
109
+ except KeyboardInterrupt:
110
+ print("htvl: stopped")
111
+ return 0
112
+
113
+
114
+ def _cmd_check(args: argparse.Namespace) -> int:
115
+ for name in args.files:
116
+ path = Path(name)
117
+ templates = load_templates(path.parent / "templates")
118
+ sys.stdout.write(
119
+ compile_source(
120
+ path.read_text(encoding="utf-8"),
121
+ templates,
122
+ modern=args.modern,
123
+ )
124
+ )
125
+ return 0
126
+
127
+
128
+ _STARTER_INDEX = """\
129
+ $DOCTYPE_htvl
130
+ $htvl
131
+ $head
132
+ $headtags
133
+ $_head
134
+ $body
135
+ $(nav 'navbar navbar-expand-md bg-dark navbar-dark')
136
+ $(a 'navbar-brand' './' 'Home' '' '' '' '')$_a
137
+ $(ul 'navbar-nav mr-auto')
138
+ $_ul
139
+ $_nav
140
+
141
+ $(div 'container')
142
+ $(h 1 '' '' 'Hello HTVL' '')
143
+ $(p '' '' 'This page was compiled from index.htvl by the HTVL compiler.' '')
144
+ $_p
145
+ $_div
146
+ $_body
147
+ $_htvl
148
+ """
149
+
150
+ _STARTER_TEMPLATE = """\
151
+ $(title '' 'My HTVL page')
152
+ $meta
153
+ $(link 'stylesheet' 'text/css' './assets/style.css')
154
+ """
155
+
156
+
157
+ def _cmd_new(args: argparse.Namespace) -> int:
158
+ root = Path(args.name)
159
+ if root.exists():
160
+ print(f"htvl: error: '{root}' already exists", file=sys.stderr)
161
+ return 1
162
+ (root / "templates").mkdir(parents=True)
163
+ (root / "assets").mkdir(parents=True)
164
+ (root / "index.htvl").write_text(_STARTER_INDEX, encoding="utf-8")
165
+ (root / "templates" / "headtags.htvl").write_text(
166
+ _STARTER_TEMPLATE, encoding="utf-8"
167
+ )
168
+ (root / "assets" / "style.css").write_text(
169
+ "body { font-family: system-ui, sans-serif; }\n", encoding="utf-8"
170
+ )
171
+ print(f"htvl: created project '{root}'")
172
+ print(f"htvl: next: cd {root} && htvl compile")
173
+ return 0
174
+
175
+
176
+ def _cmd_list(args: argparse.Namespace) -> int:
177
+ print("Tags")
178
+ print("----")
179
+ for name in sorted(ELEMENTS):
180
+ element = ELEMENTS[name]
181
+ print(f" {element.signature}")
182
+ print()
183
+ print("Variables")
184
+ print("---------")
185
+ for name in sorted(VARIABLES):
186
+ value = VARIABLES[name].replace("\n", "\\n")
187
+ if len(value) > 46:
188
+ value = value[:43] + "..."
189
+ print(f" ${name} -> {value}")
190
+ return 0
191
+
192
+
193
+ _COMMANDS = {
194
+ "compile": _cmd_compile,
195
+ "check": _cmd_check,
196
+ "new": _cmd_new,
197
+ "list": _cmd_list,
198
+ }
199
+
200
+
201
+ def main(argv: Optional[List[str]] = None) -> int:
202
+ parser = _build_parser()
203
+ args = parser.parse_args(argv)
204
+ handler = _COMMANDS[args.command]
205
+ try:
206
+ return handler(args)
207
+ except HtvlError as exc:
208
+ print(f"htvl: error: {exc}", file=sys.stderr)
209
+ return 1
210
+ except OSError as exc:
211
+ print(f"htvl: error: {exc}", file=sys.stderr)
212
+ return 1
213
+
214
+
215
+ if __name__ == "__main__": # pragma: no cover
216
+ sys.exit(main())