Jacaré for VS CodeOfficial language support for Jacaré Publisher: heberalmeida Features
InstallationVisual Studio Marketplace
Or install from the command line:
From source (monorepo)
Development mode
SnippetsIn a
Navigation snippets (
|
| Prefix | Aliases | Inserts |
|---|---|---|
jcr-nav-lazy |
lazy-screen |
Lazy createNav screen entry |
jcr-nav-screen |
screen-entry |
Eager screen() entry + import |
jcr-create-nav |
createNav |
Full createNav + createRoute scaffold |
Pair jcr-screen in the .jcr page with jcr-nav-lazy in nav.js.
Lab command
Command Palette → Jacaré: Open Lab Lesson for Symbol
- Cursor on a known symbol (
pulse,#if,createNav,bind-value, …) → opens that Lab lesson - Otherwise → QuickPick of all lessons
Live Lab: https://jacarejs.github.io/core/lab/
Template expression style
Both forms are reactive. Prefer the bare call when nothing from a #for (or other local) needs capturing:
${cart.count()}
${t('home.lead')}
class-on=${open()}
Use an arrow when the expression must close over a loop item or handler argument:
#for items() as item (item.id)
<span>${() => label(item.id)}</span>
<button on-click=${() => remove(item.id)}>×</button>
#end
jacare check warns on redundant nullary arrows (--strict-style to fail CI). See syntax — Template bindings.
Snippet examples
Component scaffold (jcr-component):
export <contract>
props: {
label: { type: 'string', required: true }
}
emits: ['change']
</contract>
export <view>
<div class="component">
</div>
</view>
export <style>
.component {
}
</style>
Contract (jcr-contract):
export <contract>
props: {
label: { type: 'string', required: true }
}
pulses: {
count: 'number'
}
slots: ['default', 'actions']
emits: ['change']
</contract>
Pulse bag (jcr-bag) / Mesh sugar (jcr-mesh) / Contract links (jcr-links):
export const cart = createBag('cart', () => { /* … */ })
export <view>
<span>${@cart/count}</span>
</view>
Island host (jcr-island):
import { mountIsland } from '@jacare/core/island'
import Widget from './Widget.jcr'
const dispose = mountIsland('#slot', Widget, {
props: { start: 0 },
shadow: true,
})
Control flow (jcr-if, jcr-for):
#if show()
<p>Visible</p>
#else
<p>Hidden</p>
#end
#for items() as item (item.id)
<li>${item.label}</li>
#end
Tab through placeholders to fill names, types, and handlers.
Language support
File extension
| Extension | Language ID | Aliases |
|---|---|---|
.jcr |
jacare |
Jacaré, jacare, jcr |
Jacaré files are plain JavaScript modules. The extension highlights both the script and embedded templates.
Highlighted syntax
JavaScript module
Tagged template
import { signal, view } from '@jacare/core'
const count = signal(0)
function increment() {
count.update((n) => n + 1)
}
export default view`
<div class="counter">
<p>${count}</p>
<button on-click=${increment}>+1</button>
</div>
`
View block (HTML-style alternative)
import { signal } from '@jacare/core'
const count = signal(0)
function increment() {
count.update((n) => n + 1)
}
export <view>
<div class="counter">
<p>${count}</p>
<button on-click=${increment}>+1</button>
</div>
</view>
Template contracts
Declare the component surface with export <contract>. The compiler checks parents with jacare check (and Vite transform) — no runtime PropTypes. Use bind-value for model: true props; :value is rejected.
export <contract>
props: { label: 'string' }
pulses: { count: 'number' }
slots: ['default', 'actions']
emits: ['inc']
</contract>
export <view>
<p>${label}: ${count}</p>
<slot name="actions" />
<button on-click=${() => emit('inc')}>+</button>
</view>
Parent usage (validated at check time):
<Counter :label=${'Score'} :count=${score} on-inc=${() => score.update((n) => n + 1)}>
<button slot="actions">Reset</button>
</Counter>
| Contract field | Meaning |
|---|---|
props |
Accepted props ('string' or { type, required, default, model }) |
pulses |
Props expected to be pulses/signals |
slots |
Slot names (default → children) |
emits |
Events via emit('name') — parent listens with on-name |
links |
Mesh aliases { alias: { from: 'bag.key', mode: 'read'\|'write'\|'mirror' } } |
See the API — Template contracts and Pulse bags sections for the full reference.
Pulse Mesh (shared state)
Jacaré-native shared pulses — highlight and snippets for bags, contract links, and ${@bag/key} address sugar.
import { createBag, pulse, ripple } from '@jacare/core'
export const cart = createBag('cart', () => {
const count = pulse(0)
return { count, bump: () => ripple(() => count.update((n) => n + 1)) }
})
export <view>
<span>${cart.count}</span>
<span>${@cart/count}</span>
<button type="button" on-click=${@cart/bump}>+</button>
</view>
export <contract>
links: {
count: { from: 'cart.count', mode: 'read' }
}
</contract>
export <view>
<span>${count}</span>
</view>
Template directives
view`
#if show()
<p>Visible</p>
#else
<p>Hidden</p>
#end
#for items() as item (item.id)
<li>${item.label}</li>
#end
`
Components and slots
view`
<Card :title=${title}>
<p>Slot content</p>
</Card>
`
Scoped styles
export <style>
.card { padding: 1rem; }
.title { font-weight: bold; }
</style>
Scope names (for theme authors)
| Scope | Used for |
|---|---|
source.jacare |
Root language scope |
keyword.control.directive.jacare-ts |
// @jacare-ts pragma (green in Jacaré scope themes) |
meta.embedded.block.typescript.jacare |
Script region when the file starts with // @jacare-ts |
keyword.control.jacare |
#if, #for, #end, @each, etc. |
entity.name.tag.jacare |
view, style, and contract block tags |
entity.name.tag.jacare.contract |
<contract> / </contract> specifically |
keyword.other.contract.jacare |
Contract fields: props, pulses, slots, emits, forwards, links, from, mode |
variable.other.mesh-address.jacare |
Mesh / route address sugar ${@bag/key} · ${@route/id} |
entity.name.type.tag.jacare |
PascalCase components |
entity.name.tag |
HTML elements (div, slot, button, view, style, …) |
entity.other.attribute-name |
bind-*, on-*, class-*, jacare-*, data-jacare-*, :prop |
meta.embedded.expression.jacare |
${expression} inside templates |
meta.contract.jacare |
Entire export <contract>…</contract> block |
source.css |
Content inside style blocks |
TypeScript coloring
| File / marker | Highlighting |
|---|---|
Foo.jcr (default) |
Script as JavaScript |
Foo.jcr starting with // @jacare-ts |
Script as TypeScript (source.ts) · pragma in Jacaré green |
Foo.jcr.ts sibling |
Full TypeScript language mode + Jacaré file icon |
Optional color themes (Preferences: Color Theme):
- Jacaré scopes (dark) — accent for
@jacare-ts, directives, tags, mesh - Jacaré scopes (light) — same scopes for light UI
Snippets: jcr-ts (pragma scaffold) · jcr-ts-sibling in .jcr.ts files.
File icons
The extension contributes the Jacaré Icons file icon theme and sets it as the default when installed.
| File type | Icon |
|---|---|
.jcr |
Jacaré logo |
.jcr.ts |
Jacaré logo (typed sibling) |
| Other files | Minimal generic file/folder icons |
If icons do not appear:
- Open Command Palette (
Cmd+Shift+P/Ctrl+Shift+P) - Run Preferences: File Icon Theme
- Select Jacaré Icons
- Run Developer: Reload Window
Configuration
The extension works out of the box. No settings are required.
To use another file icon theme while keeping syntax highlighting, pick any theme in Preferences: File Icon Theme. Language highlighting for .jcr files remains active.
Recommended workspace settings
Optional settings.json for Jacaré projects:
{
"files.associations": {
"*.jcr": "jacare"
},
"editor.quickSuggestions": {
"strings": true
}
}
What this extension does not include
| Not included | Alternative |
|---|---|
| IntelliSense / autocomplete | Use snippets (jcr-*) plus TypeScript with jacare.d.ts in your project |
| Formatting | Format the JavaScript parts with your Prettier/ESLint setup |
Cross-file contract / Mesh links check |
Use jacare check from @jacare/cli for workspace-wide validation |
Related documentation
- Jacaré repository
- API reference — template contracts · Pulse bags / Mesh
- Jacaré Lab — interactive demos including Pulse bags
- Main README
- Syntax guide
- Live demos — Todo · Showcase · Scale BMI · Lab
- Compiler docs
License
MIT © Heber Almeida