This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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.
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 testssrc/index.ts— CLI entry point; delegates tocommandLine.tsfor argument parsing andpipeline.tsfor 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,--dryrunmode,--analyzemode (per-file size table + budget exit code),--manifest(companion JSON file). Also exportsOverBudgetError(thrown when--maxsize/--maxgzipsizeis exceeded) andPreviousManifestFiletype.src/commandLine.ts— CLI parsing (nativeprocess.argv), RC files, npm variable interpolation, C++ identifier validation. ExportsIRcFileConfig,validateBasePath(),loadRcFileConfig(), andparseArguments()for use by the Vite plugin and tests.src/initCommand.ts— Interactivenpx svelteesp32 initwizard that creates.svelteesp32rc.jsonsrc/vitePlugin.ts— Vite plugin with two exclusive modes. Exported via the./vitepackage entry. RunsrunPipeline()incloseBundle(). RC file mode:svelteESP32()orsvelteESP32('/path/to/rc.json')— loads all settings from the RC file;outputfilein RC file is required. Plugin options mode:svelteESP32({ output, ... })— uses the options object exclusively; RC file is completely ignored;outputis required. The two modes do not merge.src/file.ts— Glob scanning, SHA256 hashing, duplicate detection, index.html validation. ReturnsMap<string, FileData>whereFileData = { 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.--excludepatterns are passed totinyglobby'signoreoption (no separatepicomatchcall).src/cppCode.ts— Shared C++ code generation utilities: common header, data arrays, ETag arrays, manifest, hook section,sw()switch helper; used by all engine modulessrc/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 messagessrc/consoleColor.ts— ANSI terminal color helpers (greenLog,yellowLog,redLog,cyanLog) used by pipeline outputsrc/cliInit.mts— Thin ESM entry point fornpm run dev:init; callsrunInit()frominitCommand.ts
- psychic — PsychicHttpServer V2 API, ESP32 only
- async — ESPAsyncWebServer, ESP32/ESP8266, PROGMEM
- espidf — Native ESP-IDF,
unsigned chardata arrays with(const char *)casts - webserver — Arduino WebServer, ESP32, PROGMEM, synchronous (requires
handleClient()in loop)
File Collection → MIME/SHA256 → Gzip (level 9, >1024B, >15% reduction) → TypeScript code generation (per-engine modules) → C++ header
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).
- Tri-state
etag/gzip: "always", "never", or "compiler" (C++#ifdefdirectives) - ESP-IDF ETag: malloc/free with NULL check (HTTP 500 on failure)
- File manifest:
{{definePrefix}}_FileInfostruct,{{definePrefix}}_FILES[]array, always generated onFileServedhook: weak function{{definePrefix}}_onFileServed(path, statusCode), called on 200 and 304//config:comment for traceabilityisDefaultmatching: strict=== 'index.html' || === 'index.htm'--spacatch-all: psychic addsserver->on("{{basePath}}/*", ...)only when basePath is set (no-basePath case handled bydefaultEndpoint); async/webserver addserver->onNotFound(...)with optional basePath prefix guard; espidf useshttpd_register_err_handler(HTTPD_404_NOT_FOUND, spa_handler_*)- Data arrays:
static const uint8_t data_*[]/static const uint8_t datagzip_*[]—staticprevents 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 keepsstaticlinkage {{definePrefix}}_MAX_URI_HANDLERS: psychic engine only;#defineset tosources.length + 5for use inserver.config.max_uri_handlers- Per-source cache time:
cacheTimeis computed per file intransformSourceToTemplateData— HTML files usecachetimeHtml ?? cachetime, non-HTML usecachetimeAssets ?? cachetime
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.tsusesmakeFileData()helper at outer scopetest/unit/changesummary.test.tstestsformatChangeSummaryandcreateSourceEntryvia dynamic import ofsrc/index.ts(usesvi.resetModules()inbeforeEach)
- 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.
- 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
curlyrule:"multi"— braces required only for multi-statement blocks. Single-statementif/else/formust NOT have braces.