"""
All the drafts are built by the build-specs workflow itself.
This handles the rest of the work:
* creates a root page that just redirects to the draft server root listing.
* creates symlinks for unlevelled urls, linking to the appropriate levelled folder
* builds timestamps.json, which provides a bunch of metadata about the specs which is consumed by some W3C tooling.
"""
import json
import os
import os.path
import re
import sys
import subprocess
from collections import defaultdict
import bikeshed
from html.parser import HTMLParser
def title_from_html(file):
class HTMLTitleParser(HTMLParser):
def __init__(self):
super().__init__()
self.in_title = False
self.title = ""
self.done = False
def handle_starttag(self, tag, attrs):
if tag == "title":
self.in_title = True
def handle_data(self, data):
if self.in_title:
self.title += data
def handle_endtag(self, tag):
if tag == "title" and self.in_title:
self.in_title = False
self.done = True
self.reset()
parser = HTMLTitleParser()
with open(file, encoding="UTF-8") as f:
for line in f:
parser.feed(line)
if parser.done:
break
if not parser.done:
parser.close()
return parser.title if parser.done else None
def get_date_authored_timestamp_from_git(path):
source = os.path.realpath(path)
proc = subprocess.run(["git", "log", "-1", "--format=%at", source],
capture_output = True, encoding = "utf_8")
return int(proc.stdout.splitlines()[-1])
def get_bs_spec_metadata(folder_name, path):
spec = bikeshed.Spec(path)
spec.assembleDocument()
level = int(spec.md.level) if spec.md.level else 0
if spec.md.shortname == "css-animations-2":
shortname = "css-animations"
elif spec.md.shortname == "css-gcpm-4":
shortname = "css-gcpm"
elif spec.md.shortname == "css-transitions-2":
shortname = "css-transitions"
elif spec.md.shortname == "scroll-animations-1":
shortname = "scroll-animations"
else:
# Fix CSS snapshots (e.g. "css-2022")
snapshot_match = re.match(
"^css-(20[0-9]{2})$", spec.md.shortname)
if snapshot_match:
shortname = "css-snapshot"
level = int(snapshot_match.group(1))
else:
shortname = spec.md.shortname
return {
"timestamp": get_date_authored_timestamp_from_git(path),
"shortname": shortname,
"level": level,
"title": spec.md.title,
"workStatus": spec.md.workStatus
}
def get_html_spec_metadata(folder_name, path):
match = re.match("^([a-z0-9-]+)-([0-9]+)$", folder_name)
if match and match.group(1) == "css":
shortname = "css-snapshot"
title = f"CSS Snapshot {match.group(2)}"
else:
shortname = match.group(1) if match else folder_name
title = title_from_html(path)
return {
"shortname": shortname,
"level": int(match.group(2)) if match else 0,
"title": title,
"workStatus": "completed" # It's a good heuristic
}
def create_symlink(shortname, spec_folder):
"""Creates a