Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions polyglot-utils/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Language-specific files to ignore
__pycache__/
*.pyc
.venv/
.ruby-version
122 changes: 122 additions & 0 deletions polyglot-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Preact ISO URL Pattern Matching - Polyglot Utils

Multi-language implementations of URL pattern matching utilities for building bespoke server setups that need to preload JS/CSS resources or handle early 404 responses.

## Use Case

This utility is designed for server languages that **cannot do SSR/prerendering** but still want to provide better experiences. It enables servers to:

- **Add preload head tags** for JS,CSS before serving HTML
- **Return early 404 pages** for unmatched routes
- **Generate dynamic titles** based on route parameters

## How can I implement preloading of JS, CSS?

Typical implementation flow:

1. **Build-time Setup:**
- Write your routes as an array in a JS file
- Create a build script that exports route patterns and entry files to a `.json` file
- Configure your frontend build tool to output a `manifest` file mapping entry files to final fingerprinted/hashed output JS/CSS files and dependencies

2. **Server-time Processing:**
- Load the JSON route file when a request comes in
- Match the requested URL against each route pattern until you find a match
- Once matched, you have the source entry `.jsx` file
- Load the build manifest file to find which JS chunk contains that code and its dependency files
- Generate `<link rel="preload">` tags for each dependency (JS, CSS, images, icons)
- Inject those head tags into the HTML before serving

3. **Result:**
- Browsers start downloading critical resources immediately
- Faster page loads without full SSR complexity
- Early 404s for invalid routes

### Example - preloading of JS, CSS

Here's how you might integrate this into a server setup:

### 1. Route Configuration (routes.json)
```json
[
{
"path": "/users/:userId/posts",
"component": "pages/UserPosts.jsx",
"title": "Posts by :userId"
},
{
"path": "/products/:category/:id",
"component": "pages/Product.jsx",
"title": "Product :id"
}
]
```

### 2. Build Manifest (manifest.json)
```json
{
"pages/UserPosts.jsx": {
"file": "assets/UserPosts-abc123.js",
"css": ["assets/UserPosts-def456.css"],
"imports": ["chunks/shared-ghi789.js"]
}
}
```

### 3. Server Implementation
```python
# Python example
import json

routes = json.load(open('routes.json'))
manifest = json.load(open('manifest.json'))

def handle_request(url_path):
for route in routes:
matches = preact_iso_url_pattern_match(url_path, route['path'])
if matches:
# Generate preload tags
component = route['component']
entry_info = manifest[component]

preload_tags = []
for js_file in [entry_info['file']] + entry_info.get('imports', []):
preload_tags.append(f'<link rel="modulepreload" crossorigin href="{js_file}">')

for css_file in entry_info.get('css', []):
preload_tags.append(f'<link rel="stylesheet" crossorigin href="{css_file}">')
# Generate dynamic title
title = route['title']
for param, value in matches['params'].items():
title = title.replace(f':{param}', value)

return {
'preload_tags': preload_tags,
'title': title,
'params': matches['params']
}

# No match found - return early 404
return None
```

This approach gives you the performance benefits of resource preloading without the complexity of full server-side rendering!

## Available Languages

Go, PHP, Python and Ruby.

Find the corresponding language's sub-directory. Each language has a README that contains usage examples and API reference.

## Running Tests

```bash
# Run all tests across all languages
./run_tests.sh

# Or run individual language tests
cd go && go test -v
cd python && python3 test_preact_iso_url_pattern.py
cd ruby && ruby test_preact_iso_url_pattern.rb
cd php && php test_preact_iso_url_pattern.php
```
67 changes: 67 additions & 0 deletions polyglot-utils/go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Go Implementation

URL pattern matching utility for Go servers.

## Setup

Code tested on Go 1.24.x.

```sh
# If using in a project, initialize go module
go mod init myproject
# No third party dependencies needed. Just run the tests or use the function directly
```

## Running Tests

```sh
go test -v
```

## Usage

```go
package main

import "fmt"

func main() {
matches := preactIsoUrlPatternMatch("/users/test%40example.com/posts", "/users/:userId/posts", nil)
if matches != nil {
fmt.Printf("User ID: %s\n", matches.Params["userId"]) // Output: test@example.com
}
}
```

## Function Signature

```go
func preactIsoUrlPatternMatch(url, route string, matches *Matches) *Matches
```

### Parameters

- `url` (string): The URL path to match
- `route` (string): The route pattern with parameters
- `matches` (*Matches): Optional pre-existing matches to extend

### Return Value

Returns a `*Matches` struct on success, or `nil` if no match:

```go
type Matches struct {
Params map[string]string
Rest string
}
```

## Route Patterns

| Pattern | Description | Example |
|---------|-------------|---------|
| `/users/:id` | Named parameter | `{id: "123"}` |
| `/users/:id?` | Optional parameter | `{id: ""}` |
| `/files/:path+` | Required rest parameter | `{path: "docs/readme.txt"}` |
| `/static/:path*` | Optional rest parameter | `{path: "css/main.css"}` |
| `/static/*` | Anonymous wildcard | `{Rest: "/images/logo.png"}` |
3 changes: 3 additions & 0 deletions polyglot-utils/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module myproject

go 1.13
122 changes: 122 additions & 0 deletions polyglot-utils/go/preact_iso_url_pattern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Run program: go run preact-iso-url-pattern.go

package main

import (
// "fmt"
"net/url"
"regexp"
"strings"
)

type Matches struct {
Params map[string]string `json:"params"`
Rest string `json:"rest,omitempty"`
}

func preactIsoUrlPatternMatch(urlStr, route string, matches *Matches) *Matches {
if matches == nil {
matches = &Matches{
Params: make(map[string]string),
}
}
urlParts := filterEmpty(strings.Split(urlStr, "/"))
routeParts := filterEmpty(strings.Split(route, "/"))

for i := 0; i < max(len(urlParts), len(routeParts)); i++ {
var m, param, flag string
if i < len(routeParts) {
re := regexp.MustCompile(`^(:?)(.*?)([+*?]?)$`)
matches := re.FindStringSubmatch(routeParts[i])
if len(matches) > 3 {
m, param, flag = matches[1], matches[2], matches[3]
}
}

var val string
if i < len(urlParts) {
val = urlParts[i]
}

// segment match:
if m == "" && param != "" && param == val {
continue
}

// /foo/* match
if m == "" && val != "" && flag == "*" {
matches.Rest = "/" + strings.Join(urlParts[i:], "/")
break
}

// segment mismatch / missing required field:
if m == "" || (val == "" && flag != "?" && flag != "*") {
return nil
}

rest := flag == "+" || flag == "*"

// rest (+/*) match:
if rest {
decodedParts := make([]string, len(urlParts[i:]))
for j, part := range urlParts[i:] {
decoded, err := url.QueryUnescape(part)
if err != nil {
decoded = part // fallback to original if decode fails
}
decodedParts[j] = decoded
}
val = strings.Join(decodedParts, "/")
} else if val != "" {
// normal/optional field: decode val (like JavaScript does)
decoded, err := url.QueryUnescape(val)
if err != nil {
decoded = urlParts[i]
}
val = decoded
}

matches.Params[param] = val

if rest {
break
}
}

return matches
}

func filterEmpty(s []string) []string {
var result []string
for _, str := range s {
if str != "" {
result = append(result, str)
}
}
return result
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

// Example usage:
// func main() {
// params := &Matches{Params: make(map[string]string)}
// fmt.Println(preactIsoUrlPatternMatch("/foo/bar%20baz", "/foo/:param", params))
//
// params := &Matches{Params: make(map[string]string)}
// fmt.Println(preactIsoUrlPatternMatch("/foo/bar/baz", "/foo/*"))
//
// params := &Matches{Params: make(map[string]string)}
// fmt.Println(preactIsoUrlPatternMatch("/foo", "/foo/:param?"))
//
// params := &Matches{Params: make(map[string]string)}
// fmt.Println(preactIsoUrlPatternMatch("/foo/bar", "/bar/:param"))
//
// params := &Matches{Params: make(map[string]string)}
// fmt.Println(preactIsoUrlPatternMatch("/users/test%40example.com/posts", "/users/:userId/posts"))
// }
Loading