Sketch‑Programming (LLM Transpiler) — what is it?

Sketch‑Programming (LLM Transpiler) — what is it?

Source: Dev.to

Where It Started ## React Fatigue and Motivation ## Why LLMs Change Everything ## Sketch Programming vs Vibe Coding ## React Example ## Full Component Example ## Sketch ## Generated React Code ## VS Code Plugin ## How It Works ## CSS Sketches Example ## Nested selectors ## Media queries as properties ## Limitations ## Conclusion It all began with yet another crazy idea: trying to write my own programming language, compiler, or transpiler… That’s what happens when you get tired of forms and buttons. Writing a compiler from scratch is pretty boring. You already know the result, and what lies ahead is routine work: parsing, building an AST, and so on. I got bored quickly. Then I remembered Babel. I vaguely know its internals and am familiar with Babylon (now @babel/parser). Roughly speaking, it parses code and produces an AST. We can extend JavaScript with features from newer standards not yet supported by all browsers. Some people call it a compiler, others a transpiler — at this point, it’s mostly a matter of taste. Around the same time, I was giving internal talks about new and interesting frameworks: Adonis, Edge, Alpine, signals, Svelte, Solid, HTMX — and about why React is a hopelessly outdated framework that hasn’t achieved its original goals since 2013 (okay, that was a hot take — I keep promising to write a separate article about this). Despite not liking React, I still have to use it in at least five projects. A huge amount of code is written in it, so I’ll be dealing with it for a long time. React does have advantages — especially where the virtual DOM is used not just for markup, but for native components or alternative render targets. Still, declaring two variables just to get one reactive value using a hook became unbearable. I wanted to change how components are written. Should I write another Babel plugin? Another CoffeeScript? I wanted free syntax and simple implementation. No matter how good your AST parser is, language syntax is still limited by… syntax. You need strict rules: quotes, braces, indentation. The dream is implicit returns, declarative code, config-like syntax — and that brings us back to boring ASTs. But what if we ask an LLM to transform one text into another by examples? We describe the syntax and get JavaScript, C++, or even assembly as output — which we can then execute. LLMs change software development as dramatically as the transition from assembly to high-level languages. But with one key difference: LLMs introduce non-deterministic abstraction. We can no longer store prompts in Git and expect the same result every time. Many are now talking about agent abstractions — agents that: Here, I’m talking about a transitional approach — something between classic programming and vibe coding (where the model writes everything for you). Declaring state in React today: But the explicit state keyword improves readability — mostly for humans. The LLM can infer intent from JSX anyway, but extra context may reduce hallucinations. Less boilerplate. Better readability. Full TypeScript support. To make this usable, the transformation must happen inside the editor — not in a chat window. So I built a VS Code plugin that: It’s open source and has bugs (e.g., config not reloading). Contributions welcome. You can even invent new “dialects”, like CSS Next: ChatGPT is surprisingly good at inventing these syntaxes. But these are engineering problems, not conceptual dead ends. Sketch programming is not about killing compilers. It’s about rethinking the entry point into code. This is a transitional layer between classic programming and an agent-driven future. The real question is no longer: Which language should we write in? How do we want to express ideas in code at all? Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: sumValues args sum args Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: sumValues args sum args CODE_BLOCK: sumValues args sum args COMMAND_BLOCK: function sumValues(args: number[]): number { return args.reduce((acc, x) => acc + x, 0); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function sumValues(args: number[]): number { return args.reduce((acc, x) => acc + x, 0); } COMMAND_BLOCK: function sumValues(args: number[]): number { return args.reduce((acc, x) => acc + x, 0); } CODE_BLOCK: const [counter, setCounter] = useState<number>(0); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: const [counter, setCounter] = useState<number>(0); CODE_BLOCK: const [counter, setCounter] = useState<number>(0); CODE_BLOCK: state counter number = 0; Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: state counter number = 0; CODE_BLOCK: state counter number = 0; CODE_BLOCK: counter = 0 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: counter = 0 CODE_BLOCK: counter = 0 CODE_BLOCK: // @sketch:reactComponent // @ext:tsx Component Count props add = 0 state count = 0 effect { console.log("Component mounted"); cleanup { console.log("Cleanup"); } } <div onclick="count += add">Will add {add}</div> <div> Current count: {count} </div> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // @sketch:reactComponent // @ext:tsx Component Count props add = 0 state count = 0 effect { console.log("Component mounted"); cleanup { console.log("Cleanup"); } } <div onclick="count += add">Will add {add}</div> <div> Current count: {count} </div> CODE_BLOCK: // @sketch:reactComponent // @ext:tsx Component Count props add = 0 state count = 0 effect { console.log("Component mounted"); cleanup { console.log("Cleanup"); } } <div onclick="count += add">Will add {add}</div> <div> Current count: {count} </div> COMMAND_BLOCK: import React, { useState, useEffect } from 'react'; interface Props { add?: number; } const CountComponent: React.FC<Props> = ({ add = 0 }) => { const [count, setCount] = useState<number>(0); useEffect(() => { console.log("Component mounted"); return () => { console.log("Cleanup"); }; }, []); const handleClick = () => { setCount(prev => prev + add); }; return ( <div> <div onClick={handleClick}>Will add {add}</div> <div>Current count: {count}</div> </div> ); }; export default CountComponent; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useState, useEffect } from 'react'; interface Props { add?: number; } const CountComponent: React.FC<Props> = ({ add = 0 }) => { const [count, setCount] = useState<number>(0); useEffect(() => { console.log("Component mounted"); return () => { console.log("Cleanup"); }; }, []); const handleClick = () => { setCount(prev => prev + add); }; return ( <div> <div onClick={handleClick}>Will add {add}</div> <div>Current count: {count}</div> </div> ); }; export default CountComponent; COMMAND_BLOCK: import React, { useState, useEffect } from 'react'; interface Props { add?: number; } const CountComponent: React.FC<Props> = ({ add = 0 }) => { const [count, setCount] = useState<number>(0); useEffect(() => { console.log("Component mounted"); return () => { console.log("Cleanup"); }; }, []); const handleClick = () => { setCount(prev => prev + add); }; return ( <div> <div onClick={handleClick}>Will add {add}</div> <div>Current count: {count}</div> </div> ); }; export default CountComponent; CODE_BLOCK: // @sketch:reactComponent Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // @sketch:reactComponent CODE_BLOCK: // @sketch:reactComponent CODE_BLOCK: .card { color: black; .title { font-size: 20px; } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: .card { color: black; .title { font-size: 20px; } } CODE_BLOCK: .card { color: black; .title { font-size: 20px; } } CODE_BLOCK: .card { color: black; } .card .title { font-size: 20px; } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: .card { color: black; } .card .title { font-size: 20px; } CODE_BLOCK: .card { color: black; } .card .title { font-size: 20px; } CODE_BLOCK: .container { width: 1200px; width@max-768: 100%; } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: .container { width: 1200px; width@max-768: 100%; } CODE_BLOCK: .container { width: 1200px; width@max-768: 100%; } CODE_BLOCK: .container { width: 1200px; } @media (max-width: 768px) { .container { width: 100%; } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: .container { width: 1200px; } @media (max-width: 768px) { .container { width: 100%; } } CODE_BLOCK: .container { width: 1200px; } @media (max-width: 768px) { .container { width: 100%; } } - fetch designs from Figma, - create Jira tickets, - deploy projects. - runs on save, - sends sketch files to ChatGPT via API, - replaces them with valid code. - Initialize a project - A sketch/ directory contains syntax definitions (Markdown) - A mirrored src/ directory outputs valid code - Files include tags like: - creates an OpenAI Assistant, - uploads sketch definitions into a vector store, - uses them as transformation rules. - LLMs are non-deterministic - It’s slower than a local transpiler - Syntax highlighting is hard - Caching and reproducibility matter - stop rigidly binding to formal syntax; - describe intent, structure, and patterns; - delegate mechanical transformation to the model. - you have source text, - explicit transformation rules, - predictable output format.