| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- using Spry;
- using Inversion;
- /**
- * StaticResourcesMkssrPage - Documentation for the spry-mkssr tool
- *
- * This page explains how to use spry-mkssr to generate static resources
- * for Spry applications.
- */
- public class StaticResourcesMkssrPage : PageComponent {
-
- public const string ROUTE = "/static-resources/spry-mkssr";
-
- private ComponentFactory factory = inject<ComponentFactory>();
-
- public override string markup { get {
- return """
- <div sid="page" class="doc-content">
- <h1>spry-mkssr Tool</h1>
-
- <section class="doc-section">
- <h2>What is spry-mkssr?</h2>
- <p>
- <code>spry-mkssr</code> is a command-line tool that generates Spry Static Resource
- files from any input file. It handles compression, hashing, and metadata generation
- so your web application can serve optimized static content efficiently.
- </p>
- <p>
- The tool can generate two types of output:
- </p>
- <ul>
- <li><strong>SSR files</strong> (<code>.ssr</code>) - Pre-compiled resource files read at runtime</li>
- <li><strong>Vala source files</strong> - Embedded resources compiled into your binary</li>
- </ul>
- </section>
-
- <section class="doc-section">
- <h2>Command-Line Usage</h2>
-
- <spry-component name="CodeBlockComponent" sid="usage-code"/>
-
- <h3>Options</h3>
- <table class="doc-table">
- <thead>
- <tr>
- <th>Option</th>
- <th>Description</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><code>-o, --output=FILE</code></td>
- <td>Output file name (default: input.ssr or ClassNameResource.vala)</td>
- </tr>
- <tr>
- <td><code>-c, --content-type=TYPE</code></td>
- <td>Override content type (e.g., text/css, application/javascript)</td>
- </tr>
- <tr>
- <td><code>-n, --name=NAME</code></td>
- <td>Override resource name (default: input filename)</td>
- </tr>
- <tr>
- <td><code>--vala</code></td>
- <td>Generate Vala source file instead of SSR</td>
- </tr>
- <tr>
- <td><code>--ns=NAMESPACE</code></td>
- <td>Namespace for generated Vala class (requires --vala)</td>
- </tr>
- <tr>
- <td><code>-v, --version</code></td>
- <td>Show version information</td>
- </tr>
- </tbody>
- </table>
- </section>
-
- <section class="doc-section">
- <h2>Generating SSR Files</h2>
- <p>
- By default, spry-mkssr generates <code>.ssr</code> files that can be read
- at runtime by <code>FileStaticResource</code>:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="ssr-example"/>
-
- <p>
- The generated SSR file contains:
- </p>
- <ul>
- <li><strong>Magic header</strong> - "spry-sr\0" identifier</li>
- <li><strong>Resource name</strong> - Used in URLs</li>
- <li><strong>Content type</strong> - MIME type for HTTP headers</li>
- <li><strong>SHA-512 hash</strong> - For ETag generation</li>
- <li><strong>Encoding headers</strong> - Offset and size for each encoding</li>
- <li><strong>Compressed data</strong> - gzip, zstd, brotli, and identity</li>
- </ul>
- </section>
-
- <section class="doc-section">
- <h2>Generating Vala Source Files</h2>
- <p>
- Use the <code>--vala</code> flag to generate a Vala source file that embeds
- the resource directly into your binary:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="vala-example"/>
-
- <p>
- This creates a class that extends <code>ConstantStaticResource</code> with
- the resource data embedded as <code>const uint8[]</code> arrays.
- </p>
-
- <h3>Namespace Configuration</h3>
- <p>
- Use <code>--ns</code> to place the generated class in a namespace:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="namespace-example"/>
- </section>
-
- <section class="doc-section">
- <h2>Generated Vala Class Structure</h2>
- <p>
- The generated Vala class follows this structure:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="class-structure"/>
- </section>
-
- <section class="doc-section">
- <h2>Integrating with Meson</h2>
- <p>
- The recommended way to use spry-mkssr is with meson's
- <code>custom_target</code> to generate resources at build time:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="meson-example"/>
-
- <h3>Complete meson.build Example</h3>
- <spry-component name="CodeBlockComponent" sid="meson-complete"/>
-
- <div class="info-box">
- <p>
- <strong>💡 Tip:</strong> The <code>@INPUT@</code> and <code>@OUTPUT@</code>
- placeholders are automatically replaced by meson with the actual file paths.
- </p>
- </div>
- </section>
-
- <section class="doc-section">
- <h2>Compression Details</h2>
- <p>
- spry-mkssr compresses resources with the highest compression levels:
- </p>
-
- <table class="doc-table">
- <thead>
- <tr>
- <th>Encoding</th>
- <th>Level</th>
- <th>Best For</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>gzip</td>
- <td>9 (maximum)</td>
- <td>Universal compatibility</td>
- </tr>
- <tr>
- <td>zstd</td>
- <td>19 (maximum)</td>
- <td>Modern browsers, fast decompression</td>
- </tr>
- <tr>
- <td>brotli</td>
- <td>11 (maximum)</td>
- <td>Best compression for text assets</td>
- </tr>
- <tr>
- <td>identity</td>
- <td>N/A</td>
- <td>No compression (always included)</td>
- </tr>
- </tbody>
- </table>
-
- <p>
- Encodings are only included if they result in smaller file sizes than
- the original. The tool reports compression statistics during generation:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="compression-output"/>
- </section>
-
- <section class="doc-section">
- <h2>Content Type Detection</h2>
- <p>
- spry-mkssr automatically detects content types based on file extensions
- using GLib's content type guessing. You can override this with the
- <code>-c</code> flag:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="content-type-example"/>
- </section>
-
- <section class="doc-section">
- <h2>Resource Naming</h2>
- <p>
- By default, the resource name is derived from the input filename. For
- Vala source generation, the class name is created by converting the
- resource name to PascalCase and appending "Resource":
- </p>
-
- <table class="doc-table">
- <thead>
- <tr>
- <th>Input File</th>
- <th>Resource Name</th>
- <th>Generated Class</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>docs.css</td>
- <td>docs.css</td>
- <td>DocsResource.vala → DocsResource</td>
- </tr>
- <tr>
- <td>htmx.min.js</td>
- <td>htmx.min.js</td>
- <td>HtmxMinResource.vala → HtmxMinResource</td>
- </tr>
- <tr>
- <td>-n htmx.js htmx.min.js</td>
- <td>htmx.js</td>
- <td>HtmxResource.vala → HtmxResource</td>
- </tr>
- </tbody>
- </table>
-
- <p>
- Use the <code>-n</code> flag to set a specific resource name:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="naming-example"/>
- </section>
-
- <section class="doc-section">
- <h2>Next Steps</h2>
- <div class="nav-cards">
- <a href="/static-resources/overview" class="nav-card">
- <h3>← Static Resources Overview</h3>
- <p>Back to static resources introduction</p>
- </a>
- </div>
- </section>
- </div>
- """;
- }}
-
- public override async void prepare() throws Error {
- var usage_code = get_component_child<CodeBlockComponent>("usage-code");
- usage_code.language = "Bash";
- usage_code.code = "spry-mkssr [OPTION?] INPUT_FILE\n\n" +
- "# Examples:\n" +
- "spry-mkssr styles.css # Creates styles.css.ssr\n" +
- "spry-mkssr -o bundle.ssr app.js # Creates bundle.ssr\n" +
- "spry-mkssr --vala logo.png # Creates LogoResource.vala\n" +
- "spry-mkssr --vala --ns=MyApp styles.css # Creates StylesResource.vala in MyApp namespace";
-
- var ssr_example = get_component_child<CodeBlockComponent>("ssr-example");
- ssr_example.language = "Bash";
- ssr_example.code = "# Generate an SSR file from a CSS file\n" +
- "spry-mkssr styles.css\n" +
- "# Output: styles.css.ssr\n\n" +
- "# Generate with custom output name\n" +
- "spry-mkssr -o resources/bundle.css.ssr styles.css\n" +
- "# Output: resources/bundle.css.ssr\n\n" +
- "# The resource name in URLs will be \"styles.css\"\n" +
- "# URL: /_spry/res/styles.css";
-
- var vala_example = get_component_child<CodeBlockComponent>("vala-example");
- vala_example.language = "Bash";
- vala_example.code = "# Generate a Vala source file for embedding\n" +
- "spry-mkssr --vala styles.css\n" +
- "# Output: StylesResource.vala\n\n" +
- "# The generated class name is derived from the filename\n" +
- "# styles.css → StylesResource";
-
- var namespace_example = get_component_child<CodeBlockComponent>("namespace-example");
- namespace_example.language = "Bash";
- namespace_example.code = "# Generate with namespace\n" +
- "spry-mkssr --vala --ns=MyApp.Static styles.css\n" +
- "# Output: StylesResource.vala\n\n" +
- "# Generated class:\n" +
- "# namespace MyApp.Static {\n" +
- "# public class StylesResource : ConstantStaticResource {\n" +
- "# ...\n" +
- "# }\n" +
- "# }";
-
- var class_structure = get_component_child<CodeBlockComponent>("class-structure");
- class_structure.language = "Vala";
- class_structure.code = "// Generated by spry-mkssr\n" +
- "public class DocsResource : ConstantStaticResource {\n\n" +
- " // Resource metadata\n" +
- " public override string name { get { return \"docs.css\"; } }\n" +
- " public override string file_hash { get { return \"a1b2c3d4...\"; } }\n" +
- " public override string content_type { get { return \"text/css\"; } }\n\n" +
- " // Select best encoding based on client support\n" +
- " public override string get_best_encoding(Set<string> supported) {\n" +
- " if (supported.has(\"br\")) return \"br\";\n" +
- " if (supported.has(\"zstd\")) return \"zstd\";\n" +
- " if (supported.has(\"gzip\")) return \"gzip\";\n" +
- " return \"identity\";\n" +
- " }\n\n" +
- " // Return the data for a specific encoding\n" +
- " public override unowned uint8[] get_encoding(string encoding) {\n" +
- " switch (encoding) {\n" +
- " case \"br\": return BR_DATA;\n" +
- " case \"zstd\": return ZSTD_DATA;\n" +
- " case \"gzip\": return GZIP_DATA;\n" +
- " default: return IDENTITY_DATA;\n" +
- " }\n" +
- " }\n\n" +
- " // Embedded compressed data\n" +
- " private const uint8[] IDENTITY_DATA = { /* ... */ };\n" +
- " private const uint8[] GZIP_DATA = { /* ... */ };\n" +
- " private const uint8[] ZSTD_DATA = { /* ... */ };\n" +
- " private const uint8[] BR_DATA = { /* ... */ };\n" +
- "}";
-
- var meson_example = get_component_child<CodeBlockComponent>("meson-example");
- meson_example.language = "Meson";
- meson_example.code = "# Generate a Vala resource file from CSS\n" +
- "docs_css_resource = custom_target('docs-css-resource',\n" +
- " input: 'Static/docs.css',\n" +
- " output: 'DocsCssResource.vala',\n" +
- " command: [spry_mkssr, '--vala', '--ns=Demo.Static', \n" +
- " '-n', 'docs.css', '-c', 'text/css', \n" +
- " '-o', '@OUTPUT@', '@INPUT@']\n" +
- ")\n\n" +
- "# Then include in your executable sources:\n" +
- "executable('my-app',\n" +
- " app_sources,\n" +
- " docs_css_resource, # Add generated file\n" +
- " dependencies: [spry_dep]\n" +
- ")";
-
- var meson_complete = get_component_child<CodeBlockComponent>("meson-complete");
- meson_complete.language = "Meson";
- meson_complete.code = "# Find the spry-mkssr tool\n" +
- "spry_mkssr = find_program('spry-mkssr')\n\n" +
- "# Generate multiple resources\n" +
- "css_resource = custom_target('css-resource',\n" +
- " input: 'Static/styles.css',\n" +
- " output: 'StylesResource.vala',\n" +
- " command: [spry_mkssr, '--vala', '--ns=MyApp.Resources',\n" +
- " '-o', '@OUTPUT@', '@INPUT@']\n" +
- ")\n\n" +
- "js_resource = custom_target('js-resource',\n" +
- " input: 'Static/app.js',\n" +
- " output: 'AppResource.vala',\n" +
- " command: [spry_mkssr, '--vala', '--ns=MyApp.Resources',\n" +
- " '-c', 'application/javascript',\n" +
- " '-o', '@OUTPUT@', '@INPUT@']\n" +
- ")\n\n" +
- "# Build executable with embedded resources\n" +
- "executable('my-app',\n" +
- " 'Main.vala',\n" +
- " css_resource,\n" +
- " js_resource,\n" +
- " dependencies: [spry_dep]\n" +
- ")";
-
- var compression_output = get_component_child<CodeBlockComponent>("compression-output");
- compression_output.language = "Bash";
- compression_output.code = "$ spry-mkssr --vala docs.css\n" +
- "Compressing with gzip...\n" +
- " gzip: 16094 -> 3245 bytes (20.2%)\n" +
- "Compressing with zstd...\n" +
- " zstd: 16094 -> 2987 bytes (18.6%)\n" +
- "Compressing with brotli...\n" +
- " brotli: 16094 -> 2654 bytes (16.5%)\n" +
- "Generated: DocsResource.vala";
-
- var content_type_example = get_component_child<CodeBlockComponent>("content-type-example");
- content_type_example.language = "Bash";
- content_type_example.code = "# Automatic detection (recommended)\n" +
- "spry-mkssr styles.css # → text/css\n" +
- "spry-mkssr app.js # → application/javascript\n" +
- "spry-mkssr logo.png # → image/png\n\n" +
- "# Manual override\n" +
- "spry-mkssr -c text/plain data.json # Force text/plain\n" +
- "spry-mkssr -c application/wasm module.wasm # Custom MIME type";
-
- var naming_example = get_component_child<CodeBlockComponent>("naming-example");
- naming_example.language = "Bash";
- naming_example.code = "# Default naming\n" +
- "spry-mkssr --vala htmx.min.js\n" +
- "# Resource: \"htmx.min.js\", Class: HtmxMinResource\n\n" +
- "# Custom resource name\n" +
- "spry-mkssr --vala -n htmx.js htmx.min.js\n" +
- "# Resource: \"htmx.js\", Class: HtmxResource\n" +
- "# URL: /_spry/res/htmx.js\n\n" +
- "# This is useful for:\n" +
- "# - Hiding .min suffix from URLs\n" +
- "# - Using versioned files with clean names\n" +
- "# - Renaming resources for organization";
- }
- }
|