Skip to content

Commit 70fe674

Browse files
committed
[meta] Put back the unlevelled symlinks and timestamps.json code.
1 parent 0000ddc commit 70fe674

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed

bin/build-index.py

+194
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,200 @@
55

66

77

8+
"""Builds the CSSWG directory index.
9+
10+
It also sets up redirects from shortnames to the current work spec, by building
11+
an index.html with a <meta refresh>.
12+
"""
13+
14+
import json
15+
import os
16+
import os.path
17+
import re
18+
import sys
19+
import subprocess
20+
from collections import defaultdict
21+
22+
import bikeshed
23+
from html.parser import HTMLParser
24+
25+
def title_from_html(file):
26+
class HTMLTitleParser(HTMLParser):
27+
def __init__(self):
28+
super().__init__()
29+
self.in_title = False
30+
self.title = ""
31+
self.done = False
32+
33+
def handle_starttag(self, tag, attrs):
34+
if tag == "title":
35+
self.in_title = True
36+
37+
def handle_data(self, data):
38+
if self.in_title:
39+
self.title += data
40+
41+
def handle_endtag(self, tag):
42+
if tag == "title" and self.in_title:
43+
self.in_title = False
44+
self.done = True
45+
self.reset()
46+
47+
parser = HTMLTitleParser()
48+
with open(file, encoding="UTF-8") as f:
49+
for line in f:
50+
parser.feed(line)
51+
if parser.done:
52+
break
53+
if not parser.done:
54+
parser.close()
55+
56+
return parser.title if parser.done else None
57+
58+
59+
def get_date_authored_timestamp_from_git(path):
60+
source = os.path.realpath(path)
61+
proc = subprocess.run(["git", "log", "-1", "--format=%at", source],
62+
capture_output = True, encoding = "utf_8")
63+
return int(proc.stdout.splitlines()[-1])
64+
65+
66+
def get_bs_spec_metadata(folder_name, path):
67+
spec = bikeshed.Spec(path)
68+
spec.assembleDocument()
69+
70+
level = int(spec.md.level) if spec.md.level else 0
71+
72+
if spec.md.shortname == "css-animations-2":
73+
shortname = "css-animations"
74+
elif spec.md.shortname == "css-gcpm-4":
75+
shortname = "css-gcpm"
76+
elif spec.md.shortname == "css-transitions-2":
77+
shortname = "css-transitions"
78+
elif spec.md.shortname == "scroll-animations-1":
79+
shortname = "scroll-animations"
80+
else:
81+
# Fix CSS snapshots (e.g. "css-2022")
82+
snapshot_match = re.match(
83+
"^css-(20[0-9]{2})$", spec.md.shortname)
84+
if snapshot_match:
85+
shortname = "css-snapshot"
86+
level = int(snapshot_match.group(1))
87+
else:
88+
shortname = spec.md.shortname
89+
90+
return {
91+
"timestamp": get_date_authored_timestamp_from_git(path),
92+
"shortname": shortname,
93+
"level": level,
94+
"title": spec.md.title,
95+
"workStatus": spec.md.workStatus
96+
}
97+
98+
99+
def get_html_spec_metadata(folder_name, path):
100+
match = re.match("^([a-z0-9-]+)-([0-9]+)$", folder_name)
101+
if match and match.group(1) == "css":
102+
shortname = "css-snapshot"
103+
title = f"CSS Snapshot {match.group(2)}"
104+
else:
105+
shortname = match.group(1) if match else folder_name
106+
title = title_from_html(path)
107+
108+
return {
109+
"shortname": shortname,
110+
"level": int(match.group(2)) if match else 0,
111+
"title": title,
112+
"workStatus": "completed" # It's a good heuristic
113+
}
114+
115+
116+
def create_symlink(shortname, spec_folder):
117+
"""Creates a <shortname> symlink pointing to the given <spec_folder>.
118+
"""
119+
120+
if spec_folder in timestamps:
121+
timestamps[shortname] = timestamps[spec_folder]
122+
123+
try:
124+
os.symlink(spec_folder, shortname)
125+
except OSError:
126+
pass
127+
128+
129+
CURRENT_WORK_EXCEPTIONS = {
130+
"css-conditional": 5,
131+
"css-easing": 2,
132+
"css-grid": 2,
133+
"css-snapshot": None, # always choose the last spec
134+
"css-values": 4,
135+
"css-writing-modes": 4,
136+
"web-animations": 2
137+
}
138+
139+
# ------------------------------------------------------------------------------
140+
141+
142+
bikeshed.constants.setErrorLevel("nothing")
143+
144+
specgroups = defaultdict(list)
145+
timestamps = defaultdict(list)
146+
147+
for entry in os.scandir("."):
148+
if entry.is_dir(follow_symlinks=False):
149+
# Not actual specs, just examples.
150+
if entry.name in ["css-module"]:
151+
continue
152+
153+
bs_file = os.path.join(entry.path, "Overview.bs")
154+
html_file = os.path.join(entry.path, "Overview.html")
155+
if os.path.exists(bs_file):
156+
metadata = get_bs_spec_metadata(entry.name, bs_file)
157+
timestamps[entry.name] = metadata["timestamp"]
158+
elif os.path.exists(html_file):
159+
metadata = get_html_spec_metadata(entry.name, html_file)
160+
else:
161+
# Not a spec
162+
continue
163+
164+
metadata["dir"] = entry.name
165+
metadata["currentWork"] = False
166+
specgroups[metadata["shortname"]].append(metadata)
167+
168+
# Reorder the specs with common shortname based on their level (or year, for
169+
# CSS snapshots), and determine which spec is the current work.
170+
for shortname, specgroup in specgroups.items():
171+
if len(specgroup) == 1:
172+
if shortname != specgroup[0]["dir"]:
173+
create_symlink(shortname, specgroup[0]["dir"])
174+
else:
175+
specgroup.sort(key=lambda spec: spec["level"])
176+
177+
# TODO: This algorithm for determining which spec is the current work
178+
# is wrong in a number of cases. Try and come up with a better
179+
# algorithm, rather than maintaining a list of exceptions.
180+
for spec in specgroup:
181+
if shortname in CURRENT_WORK_EXCEPTIONS:
182+
if CURRENT_WORK_EXCEPTIONS[shortname] == spec["level"]:
183+
spec["currentWork"] = True
184+
currentWorkDir = spec["dir"]
185+
break
186+
elif spec["workStatus"] != "completed":
187+
spec["currentWork"] = True
188+
currentWorkDir = spec["dir"]
189+
break
190+
else:
191+
specgroup[-1]["currentWork"] = True
192+
currentWorkDir = specgroup[-1]["dir"]
193+
194+
if shortname != currentWorkDir:
195+
create_symlink(shortname, currentWorkDir)
196+
if shortname == "css-snapshot":
197+
create_symlink("css", currentWorkDir)
198+
199+
with open('./timestamps.json', 'w') as f:
200+
json.dump(timestamps, f, indent = 2, sort_keys=True)
201+
8202
with open("./index.html", mode='w', encoding="UTF-8") as f:
9203
f.write("""
10204
<!doctype html>

0 commit comments

Comments
 (0)