Last active
September 8, 2024 15:40
-
-
Save brablc/ce1ed86d26d593d8f9dc3589d590f5e5 to your computer and use it in GitHub Desktop.
Browser performance benchmark for htmx compatible JS libraries (alpine, surreal, hyperscript, htmx)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import os | |
import requests | |
from abc import ABC, abstractmethod | |
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/[email protected]/dist/htmx.js", ""], | |
"surreal": [ | |
"surreal.js", | |
"https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js", | |
"", | |
], | |
"hyperscript": [ | |
"hyperscript.js", | |
"https://unpkg.com/[email protected]/dist/_hyperscript.js", | |
"defer", | |
], | |
"alpinejs": ["alpine.js", "https://unpkg.com/[email protected]/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(ABC): | |
output = "" | |
library_name: str | |
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)) | |
@abstractmethod | |
def get_item(self, item_number) -> str: | |
pass | |
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) -> str: | |
return """ | |
<body> | |
<div class="container"> | |
""" | |
def list_open(self): | |
return f"<div id='{self.library_name}'>" | |
def list_close(self) -> str: | |
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 get_item(self, item_number): | |
pass | |
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