diff --git a/build.py b/build.py index e14c237..08970dd 100644 --- a/build.py +++ b/build.py @@ -4,68 +4,129 @@ Build a single self-contained HTML file from the source files. Usage: python3 build.py [output.html] - -If output is not given, defaults to 'background-standalone.html'. + python3 build.py --check build to a temp file and only verify What gets inlined: - - config.js, i18n.js (plain inside an inline block would end it early - return s.replace('' % tag - new = '' % body if old not in html: raise SystemExit('build.py: expected tag not found: ' + tag) - return html.replace(old, new, 1) + # a literal inside an inline block would end it early + return html.replace(old, '' % body.replace(']*\bsrc="([^"]+)"', html) + leftovers += [re.search(r'\bhref="([^"]+)"', tag).group(1) + for tag in re.findall(r']*>', html) + if re.search(r'\bhref="[^"]+\.css"', tag)] + if leftovers: + raise SystemExit('build.py: not self-contained, still referencing: ' + + ', '.join(sorted(set(leftovers)))) def main(): - out = sys.argv[1] if len(sys.argv) > 1 else 'background-standalone.html' - html = read('background.html') + check = '--check' in sys.argv + args = [a for a in sys.argv[1:] if not a.startswith('--')] + out = args[0] if args else 'background-standalone.html' + html = read('background.html') config_src = read('config.js') themes = parse_themes(config_src) - # embedded data preamble (before clouds3d's own scripts run) + # embedded data preamble (must run before the scene scripts) stars = json.loads(read('vendor/stars.json')) theme_css = {name: read(fname) for name, fname in themes.items()} embed = ( @@ -73,29 +134,42 @@ def main(): 'window.__starsData = %s;\n' 'window.__inlineThemeCss = %s;\n' '\n' - % (json.dumps(stars, separators=(',', ':')), - json.dumps(theme_css)) + % (json.dumps(stars, separators=(',', ':')), json.dumps(theme_css)) ) - anchor = '' + # must come before config.js so the early theme loader sees the CSS + anchor = '' + if anchor not in html: + raise SystemExit('build.py: anchor script tag not found in background.html') html = html.replace(anchor, embed + anchor, 1) - html = inline_script(html, 'config.js', read('config.js')) - html = inline_script(html, 'i18n.js', read('i18n.js')) - html = inline_script(html, 'vendor/suncalc.js', read('vendor/suncalc.js')) - html = inline_script(html, 'vendor/plane3d.js', read('vendor/plane3d.js')) - html = inline_script(html, 'vendor/clouds3d.js', read('vendor/clouds3d.js')) + for tag in INLINED_SCRIPTS: + html = inline_script(html, tag, read(tag)) - # the default stays but is - # disabled by the inline-theme loader; keep a guarded fallback anyway - html = html.replace('\n ]*>', html) + if not rep_link: + raise SystemExit('build.py: theme-css link not found in background.html') + html = html[:rep_link.start()] + '\n ' + \ + html[rep_link.end():] + + verify(html) + + if check: + with tempfile.NamedTemporaryFile('w', suffix='.html', delete=False) as f: + f.write(html) + tmp = f.name + print('OK: self-contained (%d bytes, %d themes, %d stars)' + % (len(html), len(themes), len(stars))) + os.unlink(tmp) + return with open(os.path.join(ROOT, out), 'w', encoding='utf-8') as f: f.write(html) print('Wrote %s (%d bytes)' % (out, len(html))) - print('Inlined: config.js, i18n.js, suncalc.js, clouds3d.js,') - print(' stars.json (%d entries), themes: %s' - % (len(stars), ', '.join(themes))) + print('Inlined: %s, stars.json (%d entries), themes: %s' + % (', '.join(INLINED_SCRIPTS), len(stars), ', '.join(sorted(themes)))) if __name__ == '__main__':