class

Analyzer::Javascript::Fresh

Inherits Analyzer::Javascript::JavascriptEngine < Analyzer < FileHelper < Reference < Object

Fresh is Deno's filesystem-routed framework. Routes live under routes/ and the URL is derived from the directory layout:

routes/index.tsx → GET / routes/about.tsx → GET /about routes/users/index.tsx → GET /users routes/users/[id].tsx → GET /users/{id} routes/api/users.ts → handler-driven verbs routes/[...slug].tsx → GET /{slug}

Verb shape:

  • Page files (.tsx / .jsx) with a export default component render HTML — emit GET.

  • Resource files export a handler object whose keys are HTTP verbs:

    export const handler: Handlers = {
        GET(req, ctx) { ... },
        async POST(req, ctx) { ... },
        PUT: async (req) => { ... },
    };

    Each verb-shaped key (method shorthand or property assignment) registers a route.

  • If handler is a single function (no object), Fresh dispatches every method to it — fall back to the standard handler set (GET / POST / PUT / DELETE / PATCH).

Underscore-prefixed files (_app.tsx, _layout.tsx, _404.tsx, _500.tsx, _middleware.ts) are framework plumbing — not user-facing routes — and skipped.

Out of scope for this first cut:

  • Per-handler request-helper scanning. Fresh handlers receive (req, ctx) — accurate read tracking needs cross-call value flow. Path placeholders still surface via the optimizer.
  • Route groups ((group) directories — same convention as SvelteKit). Add when fixtures show real-world usage.

Constants

API_EXTENSIONS = [".ts", ".js", ".mjs"]
DENO_MANIFEST_BASENAMES = ["deno.json", "deno.jsonc"]
EXTENSIONS = PAGE_EXTENSIONS + API_EXTENSIONS
FALLBACK_HANDLER_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"]
FRESH_IMPORT_MARKER = /\$fresh\//
HANDLER_METHOD_BODY_RES = HTTP_METHODS.map do |v| {v, /(?:^|[\{,\s])(?:async\s+)?#{v}\s*\([^)]*\)\s*\{(.*?)^\s*\}/m} end.to_h

Compiled once per verb — interpolated regex literals would otherwise be rebuilt (full PCRE2 compile) on every evaluation.

HANDLER_PROPERTY_RES = HTTP_METHODS.map do |v| {v, /(?:^|[\{,\s])#{v}\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/} end.to_h
HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]
PAGE_EXTENSIONS = [".tsx", ".jsx"]
ROOT_CONFIG_BASENAMES = ["fresh.config.ts", "fresh.config.js", "fresh.config.mjs"]

A Fresh project is anchored by fresh.config.{ts,js}, or by a Deno manifest that pulls in $fresh/. Same shape the detector already treats as authoritative.

SKIPPED_LEAVES = ["_app", "_layout", "_404", "_500", "_middleware"]

Files that are framework plumbing rather than routes.

VERB_KEY_PROPERTY_RES = HTTP_METHODS.map do |v| {v, /(^|[\{,\s])#{v}\s*:/} end.to_h
VERB_KEY_SHORTHAND_RES = HTTP_METHODS.map do |v| {v, /(^|[\{,\s])(?:async\s+)?#{v}\s*\(/} end.to_h

Class methods

tech_name
Source

Instance methods

analyze
Source
tech

Instance-side view of the same declaration. The per-file rescues live on this base class, which has no way to name the analyzer that is running inside them, so a skipped file could not be attributed to a tech. Deriving it from analyzer_for keeps the name written exactly once.

Source