@md/cf-page

An opinionated, HTML-close web framework for creating Cloudflare Pages with full local development support.

Getting Started

Ensure you have Deno installed and then run:

deno run -A jsr:@md/cf-page init

This will:

Features

⚑ TypeScript Transpilation & Bundling

Automatic TypeScript compilation with full bundling support using Deno's native bundler.

Features:

  • Automatic TypeScript to JavaScript transpilation
  • Module bundling with dependency resolution using data URI encoding
  • Minification enabled automatically in production builds
  • Support for both local and remote imports (JSR, npm, HTTP)
  • Type checking during build process
  • Self-contained builds with base64 encoded dependencies

How it works:

  • Transpilation: TypeScript files are compiled to modern JavaScript
  • Bundling: All dependencies are resolved and bundled into a single file
  • Remote imports: External dependencies are fetched and included in the bundle
  • Production optimization: Automatic minification reduces file size for production builds

Example

// src/main.ts
import { serve } from "https://deno.land/std/http/server.ts";
import { helper } from "./utils.ts";

export default function() {
  return helper("Hello TypeScript!");
}

This TypeScript file will be automatically transpiled, bundled with its dependencies, and served as optimized JavaScript.

🎨 Tailwind CSS

Built-in Tailwind CSS 4 support for utility-first styling. See the Tailwind documentation for complete class reference and usage patterns.

Features:

  • Tailwind CSS 4 integration
  • Automatic class detection and purging
  • Custom component layers support
  • Dark mode variants

πŸ—œοΈ File Minification

Intelligent file optimization that automatically reduces file sizes in production builds while preserving functionality and maintaining development-friendly source code.

Production-Only Minification:

  • CSS: PostCSS with cssnano plugin for optimal compression
  • JavaScript: Built-in minification via Deno's bundler
  • TypeScript: Compiled and minified during bundling process

CSS Minification Details:

  • Removes unnecessary whitespace and comments
  • Optimizes CSS rules and declarations
  • Merges identical selectors and rules
  • Shortens color values and units where possible
  • Only runs in production builds to maintain readable development code

JavaScript Minification:

  • Variable name shortening and code compression
  • Dead code elimination removes unused functions
  • Integrated with the bundling process for optimal results
  • Preserves source functionality while reducing bundle size

Development vs Production

// Development: Readable, unminified code
.header {
  background-color: #ffffff;
  padding: 1rem 2rem;
}

// Production: Minified and optimized
.header{background:#fff;padding:1rem 2rem}

Minification only occurs during production builds, keeping your development experience fast and debuggable.

πŸ”„ Automatic Cache-Busting

Intelligent cache-busting system that automatically adds version parameters to asset URLs, ensuring users always receive the latest versions while enabling aggressive browser caching.

Supported Elements:

  • Scripts: <script src="..."> tags
  • Stylesheets: <link rel="stylesheet"> tags
  • Images: <img src="..."> tags
  • Media: <video>, <audio>, <source>, <track> elements

Hash Generation Process:

  • Content-based hashing using file contents for accuracy
  • Base64URL encoding for URL-safe hash values
  • Handles relative (`./`, `../`) and absolute (`/`) paths
  • Only processes local assets, skips external URLs
  • Rehype plugin integration for reliable HTML processing

Before and After

<!-- Before cache-busting -->
<link rel="stylesheet" href="/style.css">
<script src="./main.js"></script>
<img src="../images/logo.png" alt="Logo">

<!-- After cache-busting -->
<link rel="stylesheet" href="/style.css?v=abc123def456">
<script src="./main.js?v=xyz789uvw012"></script>
<img src="../images/logo.png?v=mno345pqr678" alt="Logo">

Query parameters are automatically added based on file content hashes. When files change, new hashes ensure cache invalidation.

Benefits:

  • Aggressive Caching: Set long cache headers without fear of stale content
  • Instant Updates: Content changes immediately invalidate caches
  • CDN Optimization: Perfect compatibility with CDN caching strategies
  • Zero Configuration: Works automatically on all supported asset types

πŸ“„ Compile-time HTML Templating

Advanced slot-based templating system with layout inheritance that processes templates at build time, ensuring zero runtime overhead and optimal performance.

Core Features:

  • Layout Inheritance: Hierarchical +layout.html files
  • Named Slots: Content injection with <slot name="...">
  • Default Content: Fallback content when slots aren't filled
  • Directory-based: Automatic layout discovery walking up the file tree
  • Compile-time Processing: Templates resolved during build, not runtime

Layout Structure Example

<!-- src/+layout.html -->
<!DOCTYPE html>
<html>
<head>
  <title><slot name="title">Default Title</slot></title>
</head>
<body>
  <header><slot name="header"></slot></header>
  <main><slot>Default main content</slot></main>
  <footer><slot name="footer">Β© {cf:year}</slot></footer>
</body>
</html>

Page Content Example

<!-- src/about/index.html -->
<slot name="title">About Us</slot>
<slot name="header">
  <nav><a href="/">Home</a></nav>
</slot>

<h1>About Our Company</h1>
<p>This content goes into the default slot.</p>

How it Works:

  • Layout Discovery: Walks up directory tree to find nearest +layout.html
  • String Processing: Processes templates before HTML parsing to preserve structure
  • Slot Injection: Replaces elements with matching content
  • Default Handling: Uses slot default content when no replacement is provided
  • Build-time Resolution: All templates fully resolved during build process

Benefits:

  • Zero Runtime Cost: Templates compiled away during build
  • SEO Friendly: Generates complete, static HTML files
  • Maintainable: Shared layouts reduce code duplication
  • Flexible: Named slots allow precise content placement

🌍 Translation Engine

Built-in multi-language support with YAML translation files, parameter interpolation, and conditional rendering capabilities.

Features:

  • YAML-based translation files (+lang.yml)
  • Parameter interpolation with flexible syntax
  • Markdown support: Use {@md key} directive for formatted content
  • Conditional rendering: {#if condition} and {#else} blocks
  • Array iteration: {#each array as item} loops
  • Cascading translation override system
  • Auto-detection of supported languages from root +lang.yml
  • Global and page-specific translations
  • Configuration via $defaultLanguage key in language files
  • Multi-language Build: Generates separate versions for each language (e.g., /en/, /de/)

Parameter Syntax

Translation values can include parameters that are replaced at build time. The syntax supports multiple parameter types:

<!-- Basic syntax: {key, param1: value1, param2: value2} -->

<!-- String literal parameters -->
<h1>{greeting, name: "John Doe"}</h1>
<!-- Output: Hello John Doe! -->

<!-- Translation key references -->
<h1>{welcome, name: {siteName}}</h1>
<!-- Output: Welcome to My Site! -->

<!-- Nested translation keys -->
<footer>{footer.copyright, year: {cf:year}}</footer>
<!-- Output: Copyright Β© 2025 mdressler. All rights reserved. -->

<!-- Mixed parameter types -->
<p>{message, user: {userName}, time: "10:30 AM", date: {cf:year}}</p>
<!-- Output: User John Doe logged in at 10:30 AM on 2025 -->

<!-- Built-in variables -->
<div lang="{cf:lang}">{cf:path}</div>
<!-- Output: <div lang="en">/about.html</div> -->

Parameter Types:

  • String literals: Enclosed in double quotes: "value"
  • Translation keys: Enclosed in braces: {key} or {nested.key}
  • Built-in variables: {cf:lang} (current language), {cf:path} (current path), {cf:year} (current year)

More Parameter Examples

The parameter syntax is flexible and supports complex use cases:

<!-- Language file with nested structure -->
# src/+lang.yml
en:
  messages:
    greeting: "Hello {user}, welcome to {app}!"
  footer:
    copyright: "Β© {year} {company}. All rights reserved."
  products:
    price: "${amount} USD"
    
<!-- Using nested keys with parameters -->
<h1>{messages.greeting, user: "Alice", app: {siteName}}</h1>
<!-- Output: Hello Alice, welcome to My Site! -->

<!-- Multiple levels of nesting -->
<footer>{footer.copyright, year: {cf:year}, company: "ACME Corp"}</footer>
<!-- Output: Β© 2025 ACME Corp. All rights reserved. -->

<!-- Combining with conditionals -->
{#if isOnSale}
  <span class="price">{products.price, amount: "49.99"}</span>
{#else}
  <span class="price">{products.price, amount: "99.99"}</span>
{/if}

Conditional & Loop Syntax

Use conditional blocks and loops to show or hide content and iterate over arrays:

<!-- Conditional rendering based on translation values -->
{#if userLoggedIn}
  <p>Welcome back, {userName}!</p>
  <a href="/logout">Logout</a>
{#else}
  <p>Please log in to continue.</p>
  <a href="/login">Login</a>
{/if}

<!-- Array iteration with markdown support -->
{#each menuItems as item}
  <li>
    <a href="/{item}">{navigation[item]}</a>
    <!-- Use @md directive to process markdown in array items -->
    <p>{@md item} section with [additional info](/{item}/info)</p>
  </li>
{/each}

<!-- Conditional content based on feature flags -->
{#if featureEnabled}
  <div class="new-feature">
    <h3>πŸŽ‰ New Feature Available!</h3>
    <p>{newFeatureDescription}</p>
  </div>
{/if}

Note: Conditional blocks work with any translation key that resolves to a truthy/falsy value. Loop blocks work with arrays. To enable markdown processing within these blocks, use the {@md key} directive (see Markdown Processing section below).

Language Files

Create a +lang.yml file in your source directory to define languages and translations:

# src/+lang.yml
$defaultLanguage: en

global:
  siteName: "My Site"
  userLoggedIn: false
  featureEnabled: true
  menuItems: ["home", "about", "contact"]

en:
  welcome: "Welcome to {name}!"
  userName: "John Doe"
  newFeatureDescription: "Try our new dashboard with enhanced analytics!"
  navigation:
    home: "Home"
    about: "About"
    contact: "Contact"

de:
  welcome: "Willkommen bei {name}!"
  userName: "Johann MΓΌller"
  newFeatureDescription: "Probieren Sie unser neues Dashboard mit erweiterten Analysen!"
  navigation:
    home: "Startseite"
    about: "Über uns"
    contact: "Kontakt"

Markdown Processing

Process markdown-formatted content from translation values using the {@md key} directive:

<!-- Language file content -->
# src/+lang.yml
en:
  boldText: "This is **very important**"
  linkText: "Visit [our website](https://example.com)"
  codeExample: "Use the `npm install` command"
  richContent: "This has **bold** and `code` and [links](/) all together"

<!-- HTML template -->
<!-- Regular placeholders - markdown NOT processed -->
<p>{boldText}</p>  
<!-- Outputs: This is **very important** -->

<!-- Markdown directive - markdown IS processed -->
<p>{@md boldText}</p>  
<!-- Outputs: This is <strong>very important</strong> -->

<p>{@md linkText}</p>
<!-- Outputs: Visit <a href="https://example.com">our website</a> -->

<p>Command: {@md codeExample}</p>
<!-- Outputs: Command: Use the <code>npm install</code> command -->

<div>{@md richContent}</div>
<!-- Outputs: This has <strong>bold</strong> and <code>code</code> and <a href="/">links</a> all together -->

Important: Markdown processing only occurs when using the {@md key} directive. Regular translation placeholders like {key} will output raw markdown text without processing. This explicit approach gives you full control over when markdown formatting is applied.

Using Translations in HTML

<!-- src/index.html -->
<h1>{welcome, name: {siteName}}</h1>

<!-- Conditional user interface -->
{#if userLoggedIn}
  <p>Welcome back, {userName}!</p>
  <a href="/logout">Logout</a>
{#else}
  <p>Please log in to continue.</p>
  <a href="/login">Login</a>
{/if}

<!-- Dynamic navigation with arrays -->
<nav>
  <ul>
    {#each menuItems as item}
      <li>
        <a href="/{item}">{navigation[item]}</a>
        <!-- Use @md directive to process markdown formatting -->
        <small>{@md item} section</small>
      </li>
    {/each}
  </ul>
</nav>

<!-- Feature flag example -->
{#if featureEnabled}
  <div class="feature-banner">
    <h3>πŸŽ‰ New Feature Available!</h3>
    <p>{newFeatureDescription}</p>
  </div>
{/if}

πŸ–ΌοΈ Automatic Image Optimization

Intelligent image processing for optimal web delivery without quality loss.

Optimizations:

  • Lossless PNG compression
  • JPG quality optimization
  • SVG minification and cleaning
  • Automatic format selection

🎨 SASS/SCSS Transpilation

Complete SASS and SCSS preprocessing support using the official Dart Sass compiler, providing all modern SASS features with automatic compilation to optimized CSS.

Supported Syntax:

  • SCSS: CSS-compatible syntax with braces and semicolons (.scss)
  • SASS: Indented syntax without braces (.sass)
  • CSS Imports: Standard CSS @import statements
  • SASS Imports: @use and @forward for module system

Advanced Features:

  • Variables: CSS custom properties and SASS variables
  • Mixins: Reusable style patterns with parameters
  • Functions: Custom functions and built-in SASS functions
  • Nesting: Hierarchical rule nesting with parent selectors
  • Control Flow: @if, @for, @each, @while
  • Module System: Modern @use and @forward imports

Example SCSS Usage

// src/styles.scss
$primary-color: #f48120;
$border-radius: 0.5rem;

@mixin button-style($bg-color) {
  background: $bg-color;
  border-radius: $border-radius;
  padding: 0.5rem 1rem;
  
  &:hover {
    background: darken($bg-color, 10%);
  }
}

.btn-primary {
  @include button-style($primary-color);
}

Compiled automatically to optimized CSS with all SASS features resolved.

Compilation Process:

  • Dart Sass Engine: Uses the official, most up-to-date SASS implementation
  • Automatic Detection: Processes .scss and .sass files automatically
  • Import Resolution: Resolves imports and dependencies correctly
  • Error Reporting: Clear error messages with line numbers and context
  • Hot Reload Integration: Works seamlessly with CSS hot reloading

Development Benefits:

  • Modern SASS: Latest SASS features and syntax support
  • Fast Compilation: Dart Sass performance optimizations
  • Full Documentation: All SASS documentation applies
  • Production Ready: Integrates with minification and optimization pipeline

πŸ”„ Live Reload

Advanced development server with intelligent file watching that automatically rebuilds and refreshes your browser when source files change, featuring smart CSS hot-reloading and build error reporting.

File System Monitoring:

  • Source Directory Watching: Monitors entire srcDir for changes
  • Intelligent Debouncing: Prevents excessive rebuilds during rapid file changes
  • Selective Rebuilding: Only rebuilds affected files and dependencies
  • Abort Controller Integration: Cancels ongoing builds when new changes detected

Browser Communication:

  • Server-Sent Events: Uses /listen endpoint for real-time updates
  • Client Script Injection: Automatically injects reload script into HTML pages
  • Event Polling: Client polls for file change events
  • Smart Refresh Logic: Differentiates between CSS-only and full page reloads

Performance Monitoring:

  • Build time measurement and reporting
  • Per-file processing time tracking
  • Performance bottleneck identification
  • Build error detection and console reporting

Development Experience

// Start the development server
deno run -A jsr:@md/cf-page serve

// File change detected: src/style.css
// β†’ Rebuilding...
// β†’ Build complete (23ms)
// β†’ Browser refreshed (CSS only)

// File change detected: src/index.html  
// β†’ Rebuilding...
// β†’ Build complete (156ms)
// β†’ Browser refreshed (full page)

The development server provides detailed feedback about build times and refresh types, helping you understand your development workflow performance.

Smart Reload Types:

  • CSS Hot Reload: Updates stylesheets without page refresh for .css, .scss, .sass
  • Full Page Reload: Complete refresh for HTML, JavaScript, and other asset changes
  • Error Recovery: Automatically retries failed builds and reports errors

πŸ”₯ Hot CSS Reload

Advanced CSS hot-reloading that intelligently updates stylesheets without page refresh, providing the fastest possible development feedback loop.

Supported File Types:

  • CSS: Direct stylesheet updates (.css)
  • SCSS/SASS: Preprocessed styles (.scss, .sass)
  • Tailwind: Utility-first CSS framework integration
  • PostCSS: All PostCSS-processed stylesheets

Hot Reload Process:

  • File Detection: Identifies CSS-only changes vs. other file types
  • Stylesheet Removal: Removes existing elements from DOM
  • Cache Busting: Adds timestamp parameters to prevent browser caching
  • Re-injection: Adds updated stylesheets back to document head

Technical Implementation

// Hot CSS reload process
1. File change detected: src/style.scss
2. SCSS compiled to CSS
3. Browser receives "reload-css" event
4. Client script execution:
   - Remove: link[rel="stylesheet"]
   - Add: timestamp to URLs (?t=1234567890)
   - Re-inject: updated stylesheets
5. Styles update instantly ⚑

The entire process typically completes in under 50ms, providing near-instantaneous feedback.

Development Workflow:

  • Instant Feedback: See styling changes immediately without losing context
  • Rapid Iteration: Perfect for fine-tuning layouts and animations
  • Cross-browser Testing: Multiple browser windows stay in sync
  • Complex State Testing: Style modal dialogs, complex forms without re-setup

⚑ Cloudflare Function Emulation

@md/cf-page provides full local emulation of Cloudflare Pages Functions. Files named +fn.ts become API endpoints with the same API as production.

Local Development Features:

  • Perfect Emulation: Full EventContext object with request, env, and utilities
  • HTTP Methods: Support for all HTTP method handlers
  • Environment Variables: Access via context.env just like in production
  • Assets API: Local context.env.ASSETS serving
  • Cloudflare-compatible request/response handling

Quick Example

// src/api/hello/+fn.ts
export const onRequest: PagesFunction = async (context) => {
  return new Response("Hello World!");
};

For complete API documentation, see Cloudflare Pages Functions API Reference.

πŸ”’ Cloudflare Middleware Emulation

@md/cf-page supports Cloudflare Pages Middleware with full local emulation. Files named +middleware.ts run before pages and functions.

Local Middleware Features:

  • Directory-based Execution: Middleware hierarchy matches Cloudflare's path matching
  • Request Context: Full context.next() and context sharing support
  • Authentication Patterns: Perfect for protecting routes and handling auth
  • Production Parity: Same execution order as Cloudflare Pages

Quick Example

// src/admin/+middleware.ts
export const onRequest: PagesFunction = async (context) => {
  const isAuthenticated = checkAuth(context.request);
  
  if (!isAuthenticated) {
    return new Response("Unauthorized", { status: 401 });
  }

  return context.next();
};

For complete middleware documentation, see Cloudflare Pages Middleware Guide.

Deploying to Cloudflare Pages

The easiest way to deploy your @md/cf-page project is by connecting it to GitHub. Follow these steps to get started:

  1. Create a new GitHub repository
  2. Create a new Cloudflare Page
  3. Link the repository you just created
  4. Set the build command to curl -fsSL https://cf-page.mdressler.dev/build.sh | bash
  5. Set the output directory to dist
  6. "Save and Deploy" your page
  7. Push your code to the repository using the instructions from GitHub
  8. You can then also enable build caching for faster builds

Configuration

Configure @md/cf-page by adding settings to your deno.jsonc or deno.json file under the @md/cf-page key:

{
  "name": "my-site",
  "tasks": { /** Your build and dev tasks */ },
  "@md/cf-page": {
    "port": 3000,
    "srcDir": "./src",
    // Any configuration overrides; see options below
  }
}

Configuration Options

port

Type: number | Default: 3000

Port for the development server.

srcDir

Type: string | Default: "./src"

Directory containing your source files (converted to absolute path).

outDir

Type: string | Default: "./dist"

Directory where built files are output (converted to absolute path).

bundle

Type: boolean | Default: true

Whether to bundle TypeScript/JavaScript files.

npmToEsm

Type: boolean | Default: true

Convert npm: specifiers to esm.sh CDN URLs for browser compatibility.

functionName

Type: string | Default: "+fn"

File name for Cloudflare Functions (without extension).

middlewareName

Type: string | Default: "+middleware"

File name for Cloudflare Middleware (without extension).

layoutName

Type: string | Default: "+layout.html"

File name for HTML layout templates.

langfileName

Type: string | Default: "+lang.yml"

File name for YAML language/translation files.

pluginName

Type: string | Default: "+plugin.ts"

File name for YAML language/translation files.

watchDirs

Type: string[] | Default: []

Other directories for which changes should rebuild the dev server.

bindings

Type: {[envVarName: string]: { type: "D1" | "KV"; id: string }} | Default: {}

Any bindings that should be provided.

ignore

Type: Array | Default: ["**/.DS_Store"]

Glob patterns to match files that should NOT be part of the final output.

Type: boolean | Default: true

Convert relative local links (e.g. href, src) to absolute ones.

markdownToHTML

Type: boolean | Default: true

Convert .md files to .html.

API Reference

CLI Commands

init - Initialize a new project with the starter template.

deno run -A jsr:@md/cf-page init

serve - Start the development server with live reload.

deno run -A jsr:@md/cf-page serve

build - Build the project for production or development.

deno run -A jsr:@md/cf-page build