⚡ Eta IntelliSenseTyped editor tooling for fast, delightful
|
| Setting | Default | What it does |
|---|---|---|
eta.tags.open |
<% |
Opening delimiter for Eta tags. |
eta.tags.close |
%> |
Closing delimiter for Eta tags. |
eta.parse.exec |
"" |
Prefix for JavaScript execution tags. |
eta.parse.interpolate |
= |
Prefix for escaped output tags. |
eta.parse.raw |
~ |
Prefix for raw output tags. |
eta.customTags |
[] |
Custom tag prefixes handled by your Eta runtime. |
eta.customTagTypes |
{} |
Declares custom prefixes as javascript or opaque text without executing plugin code. |
eta.varName |
it |
Name of the template data variable. |
eta.useWith |
false |
Exposes top-level data properties as variables. |
eta.functionHeader |
"" |
Extra declarations injected into the virtual template file. |
eta.outputFunctionName |
output |
Name of the Eta output helper function. |
eta.async |
false |
Models the template as an async function. |
eta.autoEscape |
true |
Mirrors Eta's automatic XML-escaping mode. |
eta.autoFilter |
false |
Mirrors Eta's automatic interpolation-filter mode. |
eta.autoTrim |
[false, "nl"] |
Mirrors Eta's automatic before/after trim modes. |
eta.rmWhitespace |
false |
Mirrors Eta's global whitespace-removal mode. |
The language server can also detect simple inline configs like new Eta({ tags: ["{{", "}}"], varName: "data" }) and eta.configure({ ... }) near render calls.
Generated Runtime Manifest
Static analysis cannot resolve values produced by arbitrary application code or Eta plugins. The bundled manifest builder bridges those results through an explicitly invoked project build step. Put .eta-language-support.json at the workspace root or a nested application's root. The extension only validates and reads it; it never imports the generator, Eta config, application modules, or plugin code.
From this repository, compile first and run:
bun run compile
node out/manifest.js init --config eta.manifest.config.mjs
# Edit the scaffold, then generate from your explicitly selected config.
node out/manifest.js generate --config eta.manifest.config.mjs --root .
node out/manifest.js check --root .
When using the installed extension, replace out/manifest.js with the out/manifest.js inside its extension directory. Keep the complete out/ directory together because the CLI uses its bundled helpers and TypeScript library files. --root lets you keep the config in a tooling directory. generate executes the configuration you explicitly select; check only validates existing metadata.
Edit the scaffold to register the templates your application actually loads:
import { Eta } from "eta";
import path from "node:path";
export default ({ builder }) => {
const eta = new Eta({ views: path.join(builder.root, "views") });
builder.addInput("routes.json");
builder.addTemplate({
path: "views/profile.eta",
dataType: { file: "src/models.ts", export: "ProfileData" },
eta,
names: ["profile", "account/profile"],
});
};
Type references expand exported models into self-contained types and track their source/config dependencies. Register every runtime loader, plugin, route registry and other generator input with builder.addInput(...). Pass types and source locations rather than user data or secrets. A framework adapter can loop over its actual route/template registry and call addTemplate with each route's combined locals type. An unrelated object's .render() is never assumed to be Eta.
Use source with a .eta-generated/*.eta path for in-memory templates. Different existing generated contents require overwriteGenerated: true. Plugins that transform source, ASTs or generated functions require explicit virtualDocument.content and non-overlapping mappings; the builder rejects automatic mappings for these plugins. Mapped regions can use plugin syntax outside ordinary Eta tags. Empty templates are supported with zero mappings.
The support matrix and adapter contract describe the static boundaries and runtime path for each pattern.
VS Code validates this filename against schemas/eta-language-support.schema.json. Manifest version 1 contains:
inputs: every source/config file used to generate the manifest, with a lowercase SHA-256 hash;templates: exact template hashes plus normalized Eta options, the structural data type, data-definition locations, resolution settings, related templates, generated diagnostics, and transformed virtual TypeScript;virtualDocument.mappings: non-overlapping source-to-virtual offset segments used by completion, hover, diagnostics, definition, references, rename, signature help, and semantic tokens;templateNames: runtime-computed names and their resolved template/definition locations;symbols: stable cross-file occurrence groups for definitions, references, and rename.
All paths are normalized, workspace-relative POSIX paths. The loader rejects unknown fields, path traversal, symlink escapes, malformed types, invalid or overlapping ranges, unsupported versions, oversized files, and ambiguous symbol ownership. It also verifies every declared input and template hash. If the file is missing, invalid, stale, or does not list the open template, the language server safely falls back to its static analysis for that template.
Generate the file from your own trusted build or development process after the runtime configuration and plugins have produced their final template code. Regenerate it whenever an input or template changes. Do not place secrets or runtime values in the manifest: its contents are ordinary workspace data and may be packaged or committed.
📦 Demos
The demo/ folder contains a standalone Eta workspace with typed render calls and matching templates. Open it in VS Code to try the extension against several usage patterns:
quickstart-file-renderlayouts-and-blockspartials-and-helpersasync-renderconfig-optionsapi-surfacesyntax-edge-cases
🏗 Development
This repository uses Bun and Node.js 22.18+ (or supported Node.js 24/26 releases).
bun install
bun run compile
bun run watch
bun run test
Useful scripts:
bun run compiletype-checks and bundles the extension intoout/withtsdown.bun run watchrebuilds the bundle as source files change.bun run testcompiles and runs the Vitest suite.bun run test:uiopens the Vitest UI.bun run test:coveragecompiles and runs the suite once with source coverage.bun run test:vscodetests production providers and snippets in an isolated VS Code instance.
Vitest, its UI, and its coverage provider use matching versions. VS Code API types stay pinned to the extension's minimum supported editor version. TypeScript stays on 6.0.3 because the embedded language service requires its JavaScript compiler API; TypeScript 7 is not a drop-in replacement for that API.
🗂 Project Structure
src/
extension.ts VS Code extension entrypoint and client-side providers
server.ts Language server entrypoint
etaConfig.ts Shared Eta language option defaults and normalization
etaManifest.ts Strict generated-manifest loader and source maps
semanticTokens.ts Mapped JavaScript semantic token classification
etaScanner.ts Eta tag scanner
virtualDocument.ts Eta-to-TypeScript virtual document builder
typeInference.ts Workspace render-call analysis and `it` type inference
position.ts Position and offset helpers
lspKind.ts TypeScript-to-LSP kind mapping
logging.ts Shared extension/server logging helpers
tests/ Vitest tests
syntaxes/ TextMate grammar
snippets/ Eta snippets
templates/ Syntax fixtures
demo/ Demo workspace and scenario examples
📚 Eta Docs
🤝 Contributors
Made with contrib.rocks.