Skip to content

Latest commit

 

History

History
104 lines (77 loc) · 8.56 KB

File metadata and controls

104 lines (77 loc) · 8.56 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

TypeScript CLI tool and Vite plugin converting frontend apps (Svelte, React, Angular, Vue) into C++ header files for ESP32/ESP8266 web servers. Gzip compression, ETag support, 4 engines.

Commands

npm run build          # Build TypeScript (clean + force rebuild)
npm run fix            # Fix formatting and linting (prettier + eslint + prettier)
npm run test           # Run unit tests (vitest run)
npm run test:watch     # Run tests in watch mode
npm run test:coverage  # Coverage report
npm run all            # Fix + build + test (full validation)
npx vitest run test/unit/file.test.ts              # Run a single test file
npx vitest run test/unit/file.test.ts -t "test name" # Run a specific test by name
npx tsx src/index.ts -e psychic -s ./demo/svelte/dist -o ./output.h --etag=always --gzip=always

# Dev mode (nodemon, watches src/index.ts, targets demo/esp32)
npm run dev:psychic    # psychic engine, no etag/gzip
npm run dev:async      # async engine, etag+gzip+cachetime
npm run dev:webserver  # webserver engine, etag+gzip+cachetime
npm run dev:init       # run the init wizard via tsx (dev mode)

# PlatformIO integration tests (requires PlatformIO installed)
npm run test:esp32     # build demo/esp32 with PlatformIO
npm run test:esp32idf  # build demo/esp32idf with PlatformIO
npm run test:all       # run both PlatformIO integration tests

Architecture

Core Files

  • src/index.ts — CLI entry point; delegates to commandLine.ts for argument parsing and pipeline.ts for execution. Also re-exports pipeline utilities (createSourceEntry, formatChangeSummary, formatCompressionLog, formatAnalyzeTable, formatDryRunRoutes, formatSize, formatSizePrecise, shouldUseGzip, calculateCompressionRatio, updateExtensionGroup, PreviousManifestFile) as the programmatic/test API.
  • src/pipeline.ts — Core pipeline (runPipeline()): compression, MIME types, --dryrun mode, --analyze mode (per-file size table + budget exit code), --manifest (companion JSON file). Also exports OverBudgetError (thrown when --maxsize/--maxgzipsize is exceeded) and PreviousManifestFile type.
  • src/commandLine.ts — CLI parsing (native process.argv), RC files, npm variable interpolation, C++ identifier validation. Exports IRcFileConfig, validateBasePath(), loadRcFileConfig(), and parseArguments() for use by the Vite plugin and tests.
  • src/initCommand.ts — Interactive npx svelteesp32 init wizard that creates .svelteesp32rc.json
  • src/vitePlugin.ts — Vite plugin with two exclusive modes. Exported via the ./vite package entry. Runs runPipeline() in closeBundle(). RC file mode: svelteESP32() or svelteESP32('/path/to/rc.json') — loads all settings from the RC file; outputfile in RC file is required. Plugin options mode: svelteESP32({ output, ... }) — uses the options object exclusively; RC file is completely ignored; output is required. The two modes do not merge.
  • src/file.ts — Glob scanning, SHA256 hashing, duplicate detection, index.html validation. Returns Map<string, FileData> where FileData = { content: Buffer; hash: string }. Pre-compressed files (.gz, .brotli, .br) are skipped when an uncompressed original exists. Symlinks are not followed (followSymbolicLinks: false). Files over 50 MB are rejected before read/compress. --exclude patterns are passed to tinyglobby's ignore option (no separate picomatch call).
  • src/cppCode.ts — Shared C++ code generation utilities: common header, data arrays, ETag arrays, manifest, hook section, sw() switch helper; used by all engine modules
  • src/cppCodePsychic.ts — PsychicHttpServer engine code generation (genPsychicCpp)
  • src/cppCodeAsync.ts — ESPAsyncWebServer engine code generation (genAsyncCpp)
  • src/cppCodeWebserver.ts — Arduino WebServer engine code generation (genWebserverCpp)
  • src/cppCodeEspIdf.ts — ESP-IDF engine code generation (genEspIdfCpp)
  • src/errorMessages.ts — Framework-specific error messages
  • src/consoleColor.ts — ANSI terminal color helpers (greenLog, yellowLog, redLog, cyanLog) used by pipeline output
  • src/cliInit.mts — Thin ESM entry point for npm run dev:init; calls runInit() from initCommand.ts

Engines

  • psychic — PsychicHttpServer V2 API, ESP32 only
  • async — ESPAsyncWebServer, ESP32/ESP8266, PROGMEM
  • espidf — Native ESP-IDF, unsigned char data arrays with (const char *) casts
  • webserver — Arduino WebServer, ESP32, PROGMEM, synchronous (requires handleClient() in loop)

Pipeline

File Collection → MIME/SHA256 → Gzip (level 9, >1024B, >15% reduction) → TypeScript code generation (per-engine modules) → C++ header

CLI Options

init (interactive RC file wizard), -s (source), -e (engine), -o (output), --etag (always/never/compiler), --gzip (always/never/compiler), --exclude (glob patterns, no defaults — empty by default), --basepath (URL prefix, must start with /, no trailing /), --noindexcheck, --dryrun, --analyze (per-file size table + budget pass/fail, exits 1 on over-budget, mutually exclusive with --dryrun), --spa (catch-all for SPA client-side routing), --manifest (write companion .manifest.json alongside header), --cachetime, --cachetimehtml (HTML-only max-age, overrides --cachetime), --cachetimeassets (non-HTML max-age, overrides --cachetime), --define, --espmethod, --maxsize (total uncompressed size limit, e.g. 400k), --maxgzipsize (total gzip size limit), --created (include creation timestamp), --version (embed version string in header)

RC files: .svelteesp32rc.json or .svelteesp32rc in cwd, home, or --config=path. Supports $npm_package_* interpolation. Prints a warning when loaded from cwd. outputfile in RC files must be a relative path (absolute paths throw). Boolean fields (noindexcheck, dryrun, analyze, spa, manifest, created) accept native booleans or string "true"/"false" (matching etag/gzip string behaviour).

Generated C++ Details

  • Tri-state etag/gzip: "always", "never", or "compiler" (C++ #ifdef directives)
  • ESP-IDF ETag: malloc/free with NULL check (HTTP 500 on failure)
  • File manifest: {{definePrefix}}_FileInfo struct, {{definePrefix}}_FILES[] array, always generated
  • onFileServed hook: weak function {{definePrefix}}_onFileServed(path, statusCode), called on 200 and 304
  • //config: comment for traceability
  • isDefault matching: strict === 'index.html' || === 'index.htm'
  • --spa catch-all: psychic adds server->on("{{basePath}}/*", ...) only when basePath is set (no-basePath case handled by defaultEndpoint); async/webserver add server->onNotFound(...) with optional basePath prefix guard; espidf uses httpd_register_err_handler(HTTPD_404_NOT_FOUND, spa_handler_*)
  • Data arrays: static const uint8_t data_*[] / static const uint8_t datagzip_*[]static prevents multiple-definition linker errors when included in more than one TU
  • ETag variables: static const char etag_*[] (char array, not pointer) — avoids pointer indirection and keeps static linkage
  • {{definePrefix}}_MAX_URI_HANDLERS: psychic engine only; #define set to sources.length + 5 for use in server.config.max_uri_handlers
  • Per-source cache time: cacheTime is computed per file in transformSourceToTemplateData — HTML files use cachetimeHtml ?? cachetime, non-HTML use cachetimeAssets ?? cachetime

Testing (Vitest)

Test files in test/unit/. Fixtures in test/fixtures/sample-files/.

Key patterns:

  • Mock fs with vi.mock('node:fs') and memfs
  • Dynamic imports for commandLine tests (side effects)
  • test/unit/index.test.ts uses makeFileData() helper at outer scope
  • test/unit/changesummary.test.ts tests formatChangeSummary and createSourceEntry via dynamic import of src/index.ts (uses vi.resetModules() in beforeEach)

Documentation Conventions

  • README.md "What's New" section: List only minor and major versions (e.g., v3.0.0, v2.4.0). Patch versions (e.g., v3.0.1, v3.0.2) must not appear — fold their content into the parent minor version entry or omit it.

Build Config

  • TypeScript: Target ES2023, module/moduleResolution Node16, strict mode
  • ESLint: TypeScript + Prettier + Unicorn (all rules) + simple-import-sort
  • Prettier: 120 char width, single quotes, no trailing commas
  • ESLint curly rule: "multi" — braces required only for multi-statement blocks. Single-statement if/else/for must NOT have braces.