Files
deepgrain-foundry/build.py
T

61 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Build Deepgrain Foundry into one self-contained HTML file.
The source files listed in src/manifest.json are joined in order (sim first, then the browser
code) into one classic script, so they share one global scope and 'use strict' at the top of
src/sim/base.js applies to all of it. No bundler, no dependencies.
Writes:
dist/index.html the full page: serve with python3 tools/serve.py
dist/deepgrain-foundry.html the same without <!doctype>/<head>/<body>, for hosts (like a
claude.ai artifact) that wrap the page themselves
dist/bundle-lines.json where each source file starts in dist/index.html, so a line number
from a browser error can be traced back: python3 tools/where.py LINE
"""
import json, pathlib
root = pathlib.Path(__file__).resolve().parent
src = root / 'src'
dist = root / 'dist'
man = json.loads((src / 'manifest.json').read_text())
def join(files):
"""Join files with one newline between them; return the text and [first line, file] pairs."""
parts, starts, line = [], [], 1
for f in files:
text = (src / f).read_text().rstrip('\n')
starts.append([line, f])
parts.append(text)
line += text.count('\n') + 1
return '\n'.join(parts), starts
def main():
shell = (src / man['shell']).read_text()
css, _ = join(man['css'])
js, starts = join(man['sim'] + man['client'])
sim, _ = join(man['sim'])
# Keep worker code outside the indexed script so source line mappings stay exact.
worker = '<script>const SIM_WORKER_SOURCE=' + json.dumps(sim).replace('</', '<\\/') + ';</script>'
head, tail = shell.split('//__JS__')
body = head.replace('/*__CSS__*/', css)
page = body + js + tail
page = worker + page
pre = ('<!doctype html><html><head><meta charset="utf-8">'
'<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">'
'</head><body>')
full = pre + page + '</body></html>\n'
dist.mkdir(exist_ok=True)
(dist / 'deepgrain-foundry.html').write_text(page)
(dist / 'index.html').write_text(full)
# the script starts on this line of index.html (the doctype prefix has no newlines)
js_line = worker.count('\n') + body.count('\n') + 1
(dist / 'bundle-lines.json').write_text(json.dumps(
{'page': 'dist/index.html', 'files': [[js_line + s - 1, f] for s, f in starts]}, indent=1) + '\n')
print(f'built dist/index.html ({len(full):,} bytes, {len(starts)} source files)')
if __name__ == '__main__':
main()