1+ from __future__ import annotations
2+
13import argparse
24from hashlib import sha256
35import json
911
1012from starlette .responses import Response
1113from starlette .staticfiles import StaticFiles
14+ from starlette .types import Scope
1215from typing_extensions import override
1316
17+ #: A StaticFilesManifest maps the relative path to the static files root
18+ #: (i.e. the input value of a template requiring a static file) to the relative
19+ #: path that should be rendered in a template, and the full path of the file on
20+ #: disk.
21+ StaticFilesManifest : typing .TypeAlias = dict [str , tuple [str , pathlib .Path ]]
22+
1423
15- def compile_static_files (* , destination : pathlib .Path , sources : typing . Sequence [ pathlib . Path ]) :
24+ def compile_static_files (* , destination : pathlib .Path , manifest : StaticFilesManifest ) -> None :
1625 """Compile a static directory from one or more source directories."""
1726 # This function is designed to write the static files, could be useful for serving static
1827 # files via apache/nginx/etc.
19- manifest = generate_manifest (sources )
2028 file_map : dict [str , dict [str , str ]] = {'file-map' : {}}
2129
2230 for input_filename , (hashed_relpath , source_path ) in manifest .items ():
@@ -29,7 +37,7 @@ def compile_static_files(*, destination: pathlib.Path, sources: typing.Sequence[
2937 (destination / '.gitignore' ).write_text ('*' )
3038
3139
32- def generate_manifest (sources : typing .Sequence [pathlib .Path ]) -> dict [ str , tuple [ str , pathlib . Path ]] :
40+ def generate_manifest (sources : typing .Sequence [pathlib .Path ]) -> StaticFilesManifest :
3341 """
3442 Generate a manifest which maps template_rel_path to a (hashed_relpath, full_path) tuple.
3543 """
@@ -54,7 +62,7 @@ def generate_manifest(sources: typing.Sequence[pathlib.Path]) -> dict[str, tuple
5462
5563
5664class HashedStaticFileHandler (StaticFiles ):
57- def __init__ (self , * , manifest , ** kwargs ):
65+ def __init__ (self , * , manifest : StaticFilesManifest , ** kwargs ) -> None :
5866 super ().__init__ (** kwargs )
5967 self .manifest = manifest
6068 self ._inverted_manifest = {src : path for src , path in manifest .values ()}
@@ -64,10 +72,10 @@ def lookup_path(self, path: str) -> tuple[str, os.stat_result | None]:
6472 actual_path = self ._inverted_manifest .get (path )
6573 if actual_path is None :
6674 return super ().lookup_path (path )
67- return actual_path , os .stat (actual_path )
75+ return str ( actual_path ) , os .stat (actual_path )
6876
6977 @override
70- async def get_response (self , path : str , scope ) -> Response :
78+ async def get_response (self , path : str , scope : Scope ) -> Response :
7179 response = await super ().get_response (path , scope )
7280 if response .status_code in [200 , 304 ]:
7381 response .headers ["Cache-Control" ] = "public, max-age=31536000, immutable"
@@ -95,7 +103,8 @@ def main(argv: typing.Sequence[str]) -> None:
95103
96104def handle_compile (args : argparse .Namespace ):
97105 print (f'Writing static files to { args .destination } ' )
98- compile_static_files (destination = args .destination , sources = args .source )
106+ manifest = generate_manifest (args .source )
107+ compile_static_files (destination = args .destination , manifest = manifest )
99108
100109
101110if __name__ == '__main__' :
0 commit comments