Skip to content

Instantly share code, notes, and snippets.

@brablc
Last active March 5, 2024 18:07
Show Gist options
  • Save brablc/ce1ed86d26d593d8f9dc3589d590f5e5 to your computer and use it in GitHub Desktop.
Save brablc/ce1ed86d26d593d8f9dc3589d590f5e5 to your computer and use it in GitHub Desktop.
Browser performance benchmark for htmx compatible JS libraries (alpine, surreal, hyperscript, htmx)
import argparse
import os
import requests
parser = argparse.ArgumentParser(
description="""
Generates testing files for JS libraries that are popular with htmx.
Run --setup first to download libraries for local use.
A separate file for each library will be generated - timing is done with console.time and console.timeEnd.
"""
)
parser.add_argument("--items", type=int, default=10, help="Number of items to render.")
parser.add_argument(
"--setup", action="store_true", help="Setup the tool - download libraries."
)
libraries = {
"htmx": ["htmx.js", "https://unpkg.com/htmx.org@1.9.10/dist/htmx.js", ""],
"surreal": [
"surreal.js",
"https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js",
"",
],
"hyperscript": [
"hyperscript.js",
"https://unpkg.com/hyperscript.org@0.9.12/dist/_hyperscript.js",
"defer",
],
"alpinejs": ["alpine.js", "https://unpkg.com/alpinejs@3.13.5/dist/cdn.js", "defer"],
"vanillajs": [None, None, None],
}
args = parser.parse_args()
if args.setup:
for lib in libraries:
local_url, remote_url, _ = libraries[lib]
if local_url and not os.path.exists(local_url):
print(f"Downloading {remote_url} to {local_url}")
r = requests.get(remote_url)
with open(local_url, "w") as f:
f.write(r.text)
class BaseWriter:
output = ""
def __init__(self):
self.output = ""
def reset(self):
self.output = ""
def add(self, str):
self.output += str + "\n"
def add_begin(self):
self.reset()
self.add("""
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
""")
self.add(self.script_include())
self.add("</head>")
self.add(self.body_open())
self.add(f"""
<strong>{self.library_name}</strong>
<script>console.time("{self.library_name}");</script>
""")
self.add(self.list_open())
def add_item(self, item_number):
self.add(self.get_item(item_number))
def item(self, item_number):
pass # To be implemented by subclasses
def add_end(self):
self.add(self.list_close())
self.add(self.body_close())
self.add("</html>")
def script_include(self):
local_url, _, attr = libraries[self.library_name]
if local_url:
return f'<script src="{local_url}" {attr}></script>'
return ""
def body_open(self):
return """
<body>
<div class="container">
"""
def list_open(self):
return f"<div id='{self.library_name}'>"
def list_close(self):
return "</div>"
def body_close(self):
return """
</div>
</body>
</html>"""
class AlpinejsWriter(BaseWriter):
library_name = "alpinejs"
def list_open(self):
return f"""
<div x-data id="{self.library_name}">
"""
def get_item(self, item_number):
return f"""<div x-on:click="$el.children[0].innerText = '{self.library_name}'">
<div>{item_number}</div></div>"""
def list_close(self):
return (
f"""<div x-init="console.timeEnd('{self.library_name}')"></div>"""
+ super().list_close()
)
class HyperscriptWriter(BaseWriter):
library_name = "hyperscript"
def get_item(self, item_number):
return f"""<div _="on click set innerText of the first <div/> in me to '{self.library_name}'"><div>{item_number}</div></div>"""
def list_close(self):
return (
f"""<div _="init call console.timeEnd('{self.library_name}')"></div>"""
+ super().list_close()
)
class SurrealWriter(BaseWriter):
library_name = "surreal"
def get_item(self, item_number):
return f"""<div><script>me().on('click', (ev) => {{ me(ev).children[1].innerText = '{self.library_name}'; }});</script><div>{item_number}</div></div>"""
def list_close(self):
return (
f"""<script>console.timeEnd("{self.library_name}");</script>"""
+ super().list_close()
)
class VanillajsWriter(BaseWriter):
library_name = "vanillajs"
def get_item(self, item_number):
return f"""<div><div>{item_number}</div></div>"""
def list_close(self):
return f"""
<script>
document.querySelectorAll('#{self.library_name} > div').forEach(function(div) {{
div.addEventListener('click', function(event) {{
event.currentTarget.children[0].innerText = '{self.library_name}';
}});
}});
console.timeEnd("{self.library_name}");
</script>""" + super().list_close()
class HtmxWriter(BaseWriter):
library_name = "htmx"
def add_item(self, item_number):
self.add(
f"""<div hx-on:click="this.children[0].innerText = '{self.library_name}'"><div>{item_number}</div></div>"""
)
def body_open(self):
return f"""
<body hx-on:htmx:load="console.timeEnd('{self.library_name}')">
<div>"""
for lib in libraries:
class_name = f"{lib.capitalize()}Writer"
writer_class = globals()[class_name]()
writer_class.add_begin()
for i in range(1, args.items + 1):
writer_class.add_item(i)
writer_class.add_end()
with open(f"test-{lib}.html", "w") as f:
f.write(writer_class.output)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment