How does EsJS work in the background?
¿Cómo funciona EsJS de fondo?
Voy a explorar el repositorio para entender cómo funciona EsJS internamente.
Ahora veamos la estructura principal del core:
Perfecto, ahora veamos el archivo principal:
Ahora veamos los compiladores:
Veamos ahora los plugins:
Ahora veamos el paquete esbabel que parece ser el compilador:
Perfecto. Ahora déjame ver más del generator para entender cómo genera el código JavaScript:
Excelente. Ahora veamos un ejemplo de plugin para entender la transformación completa:
Perfecto. Ahora déjame ver un ejemplo completo de un plugin:
Ahora veamos el contenido completo del archivo consola:
Excelente. Ahora veamos el archivo utils de plugins:
Perfecto. Ahora veamos el archivo principal de utils:
Perfecto. Ahora tengo una buena comprensión. Déjame crear un diagrama mental y una explicación clara. Déjame ver un test para entender mejor el flujo:
Déjame buscar un ejemplo más simple:
Perfecto. Ya tengo suficiente información. Déjame crear una explicación completa y bien estructurada:
##Cómo funciona EsJS de fondo
EsJS es un transpilador (compilador de lenguaje a lenguaje) que convierte código escrito en sintaxis española a JavaScript válido. Aquí te explico cómo funciona internamente:
###🏗️ Arquitectura General
El flujo de compilación tiene dos caminos principales:
Código EsJS → [Compilador] → JavaScript
↓
[Plugins de transformación]
↓
JavaScript optimizado
###📦 Componentes Principales
####1. Core (@es-js/core)
Es el punto de entrada principal. Define la función compile() que orquesta todo:
compile(code: string, options?: CompileOptions): string
Opciones disponibles:
from: idioma origen ('esjs' o 'js')to: idioma destino ('esjs' o 'js')compiler: 'essucrase' (por defecto) o 'esbabel'
####2. Dos Compiladores Disponibles
A) EsbabelCompiler (más simple)
- Usa el paquete
@es-js/esbabel - Realiza una conversión simple y directa
B) EssucraseCompiler (más completo, por defecto)
- Usa la librería
@es-js/compiler(essucrase) - Genera un AST (Árbol de Sintaxis Abstracta)
- Aplica plugins para transformaciones más complejas
###🔄 Flujo de Compilación Detallado
####Paso 1: Tokenización (Lexical Analysis)
El compilador esbabel primero convierte el código en tokens:
// En esbabel/src/lexer.ts
tokenize(code: string): Token[]
Los tokens identificados son:
- Identificadores (
Identifier) - nombres de variables - Palabras clave (
Keyword) -si,mientras,para, etc. - Caracteres especiales (
SpecialCharacter) - operadores - Literales de cadena (
StringLiteral) - texto entre comillas - Caracteres simples (
LeftParen,RightCurly,Dot, etc.)
Ejemplo:
Código: "var x = 5"
Tokens: [
{ type: 'Keyword', name: 'var' },
{ type: 'Identifier', name: 'x' },
{ type: 'SpecialCharacter', value: '=' },
{ type: 'SpecialCharacter', value: '5' }
]
####Paso 2: Mapeo de Palabras Clave
El compilador mantiene mapeos de diccionarios en esbabel/src/keywords.ts:
// Ejemplos de mapeos disponibles:
const keywordControl = new Map([
['si', 'if'],
['mientras', 'while'],
['para', 'for'],
['retornar', 'return'],
...
])
const supportFunction = new Map([
['consola', 'console'],
['Fecha', 'Date'],
['Numero', 'Number'],
['Promesa', 'Promise'],
...
])
####Paso 3: Generación (Code Generation)
El generador convierte los tokens nuevamente a código, pero reemplazando cada palabra clave española por su equivalente en inglés:
// En esbabel/src/generator.ts
generate(tokens: Token[], reverse = false): string
Lógica:
- Itera sobre cada token
- Si es una palabra clave/identificador, busca en el diccionario
- Si no está en el diccionario, mantiene el nombre original
- Concatena todo el código generado
Ejemplo:
Tokens → Generador → "const x = 5"
###🔌 Sistema de Plugins
Cuando se usa EssucraseCompiler, después de la compilación base, se aplican plugins que transforman el AST para casos más complejos.
Ubicación: packages/core/src/plugins/
Cada plugin es responsable de transformar un objeto/método específico:
consola→console.log(),console.error(), etc.matriz→Array.isArray(),Array.from(), etc.cadena→String.prototype.includes(), etc.fecha→Date.now(), métodos de fecha, etc.numero→Number.isNaN(),Number.parseFloat(), etc.promesa→Promise.all(),.then(), etc.mate→Math.floor(),Math.random(), etc.
Estructura de un plugin (ejemplo: consola):
// packages/core/src/plugins/consola/index.ts
export const report = () => 'Converts consola to console'
export const methods = new Map<string, string>([
['afirmar', 'assert'],
['limpiar', 'clear'],
['escribir', 'log'],
['advertencia', 'warn'],
// ... más métodos
])
export const objects = new Map([['consola', 'console']])
export function replace() {
return {
...replaceObjectStaticMethods({
from: 'consola',
to: 'console',
methods,
}),
}
}
Cómo funcionan los plugins:
- Definen mapeos (
methods,properties,staticMethods, etc.) - Usan funciones de utilidad (
replaceObjectStaticMethods, etc.) - Generan reglas de transformación que se aplican al AST
- El generador
putoutaplica estas reglas recursivamente
###💫 Funcionalidades Adicionales
####Invertibilidad de Mapeos
Los compiladores pueden trabajar en ambas direcciones:
// EsJS → JavaScript (normal)
compile(code, { from: 'esjs', to: 'js' })
// JavaScript → EsJS (reverse)
compile(code, { from: 'js', to: 'esjs' })
Esto se logra con invertMap() que invierte el diccionario de búsqueda.
####Separación de Imports
Hay lógica para extraer y manejar imports por separado:
// En src/utils.ts
splitCodeImports(code): {
codeWithoutImports,
imports,
hasImports
}
###🎯 Ejemplo Completo de Transformación
Entrada (EsJS):
var x = 5
si (x > 3) {
consola.escribir("Es mayor")
}
Proceso:
- Tokenización → Identifica palabras clave españolas
- Mapeo →
var→var,si→if,consola.escribir→console.log - Generación → Reconstruye el código
- Plugins → Aplica transformaciones complejas de métodos
Salida (JavaScript):
var x = 5
if (x > 3) {
console.log("Es mayor")
}
###📊 Resumen del Flujo
┌─────────────────────────────────────┐
│ Código en EsJS │
└────────────┬────────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ ESBABEL COMPILER │
│ ├─ Lexer: Tokenización │
│ ├─ Generator: Conversión básica │
│ └─ Diccionarios: Mapeos español→en │
└────────────┬────────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ ESSUCRASE COMPILER (opcional) │
│ ├─ AST Parser │
│ ├─ Aplicar Plugins │
│ └─ Code generation │
└────────────┬────────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ JavaScript válido │
└─────────────────────────────────────┘
Esta arquitectura permite que EsJS sea modular, extensible y bidireccional, mientras mantiene la compatibilidad completa con JavaScript moderno.
pero actualmente se utiliza EsSucrase, no EsBabel
Tienes razón, déjame revisar más a fondo cómo funciona actualmente EsSucrase:
Veo que usa @es-js/compiler. Déjame buscar más información sobre qué es essucrase y cómo se usa:
Déjame ver qué es exactamente @es-js/compiler:
Perfecto, ahora entiendo mejor. Déjame ver el repositorio de @es-js/compiler:
Excelente, ahora tengo la información clave. Veamos el flujo real en el EssucraseCompiler:
Perfecto. Ahora veamos un ejemplo real de cómo se usan los plugins. Déjame ver uno más complejo:
Perfecto, ahora tengo toda la información necesaria. Déjame escribir una explicación actualizada y precisa:
##🔧 Cómo funciona EsJS de fondo (con EsSucrase)
EsJS es un transpilador que convierte código escrito en sintaxis española a JavaScript de forma rápida y eficiente. Actualmente utiliza EsSucrase como compilador base, que es un fork especializado de Sucrase.
###📚 La cadena de herramientas
┌─────────────────────────────────────────────────────────────┐
│ @es-js/compiler (EsSucrase) │
│ ↳ Fork de Sucrase (super-fast Babel alternative) │
│ ↳ Optimizado para EsJS │
│ ↳ Hereda parser de Babel pero simplificado │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ @es-js/core (Orquestador) │
│ ├─ EssucraseCompiler (compilador principal) │
│ ├─ Plugins (transformaciones específicas del dominio) │
│ └─ Utils (utilidades generales) │
└─────────────────────────────────────────────────────────────┘
###🔄 Flujo de Compilación en Detalle
Cuando llamas a compile(code, options), sucede lo siguiente:
####Paso 1: Entrada y opciones
compile(code: string, options?: CompileOptions): string
Las opciones por defecto son:
from: 'esjs'- El código viene en sintaxis españolato: 'js'- Queremos JavaScriptcompiler: 'essucrase'- Usar EsSucrase
####Paso 2: Compilación base con EsSucrase
compileEssucrase(code: string, options?: CompileOptions): string
↓
transform(code, {
transforms: ['esjs']
}).code
@es-js/compiler (EsSucrase) realiza:
- Parse: Convierte el código en un AST (Árbol de Sintaxis Abstracta)
- Transform: Aplica la transformación
'esjs'que:- Reemplaza palabras clave españolas por equivalentes en inglés
- Transforma estructuras de sintaxis
- Generate: Regenera el código JavaScript
Este proceso es muy rápido porque:
- Solo se enfoca en EsJS (no en ES5, IE11, etc. como Babel)
- Usa un parser optimizado heredado de Babel pero simplificado
- Evita passes innecesarias
####Paso 3: Aplicación de Plugins
applyPlugins(compiled: string, toEsJS?: boolean): string
Si necesita aplicar transformaciones más complejas:
-
Parse con putout (herramienta de análisis de código):
const ast = putout.parse(code, { printer: 'recast' }) -
Aplica plugins: Cada plugin transforma el AST con reglas específicas
-
Genera código final:
putout.print(ast, { printer: 'recast' })
###🔌 El Sistema de Plugins
Los plugins son el mecanismo para transformar objetos y métodos españoles a JavaScript.
Estructura típica de un plugin:
// packages/core/src/plugins/matriz/index.ts
export const report = () => 'Converts Matriz methods to JavaScript'
// Mapeo de métodos de instancia
export const methods = new Map<string, string>([
['posicion', 'at'],
['concatenar', 'concat'],
['filtrar', 'filter'],
['mapear', 'map'],
// ... más métodos
])
// Mapeo de métodos estáticos
export const staticMethods = new Map<string, string>([
['desde', 'from'],
['esMatriz', 'isArray'],
])
// Objetos que se transforman
export const objects = new Map<string, string>([
['Matriz', 'Array'],
['Arreglo', 'Array'],
])
// Función que genera las reglas de transformación
export function replace() {
return {
...replaceObjectStaticMethods({
from: 'Matriz',
to: 'Array',
methods: staticMethods,
}),
...replaceExpressionMethods({
methods,
}),
...replaceObjects({
objects,
}),
}
}
Cómo funcionan las reglas:
// replaceObjectStaticMethods genera reglas como:
// 'Matriz.desde(__args)' → 'Array.from(__args)'
// 'Matriz.esMatriz(__args)' → 'Array.isArray(__args)'
// replaceExpressionMethods genera:
// '__a.mapear' → '__a.map'
// '__a.filtrar' → '__a.filter'
// replaceObjects genera:
// 'new Matriz()' → 'new Array()'
// 'new Arreglo()' → 'new Array()'
###📦 Lista de Plugins Disponibles
En packages/core/src/plugins/:
| Plugin | Transforma | Ejemplos |
|---|---|---|
| consola | console | consola.escribir() → console.log() |
| matriz | Array | Matriz.desde() → Array.from() |
| cadena | String | Cadena.incluye() → String.includes() |
| fecha | Date | Fecha.ahora() → Date.now() |
| numero | Number | Numero.esEntero() → Number.isInteger() |
| promesa | Promise | Promesa.todos() → Promise.all() |
| mate | Math | Mate.piso() → Math.floor() |
| booleano | Boolean | Transformaciones booleanas |
| funcion | Function | Métodos de función |
| json | JSON | JSON.analizar() → JSON.parse() |
| navegador | navigator | navegador.idioma → navigator.language |
| ventana | window | ventana.abierta() → window.open() |
| documento | Document | Métodos del DOM |
| apoderado | Proxy | Apoderado → Proxy |
| simbolo | Symbol | Métodos de símbolo |
| enterogrande | BigInt | EnteroGrande → BigInt |
| tipos | Type conversions | Conversión de tipos |
| objetos | Object | Métodos de objeto |
| soporte | Support functions | Funciones de soporte |
###🔑 Mapeos de Palabras Clave
Definidos en @es-js/esbabel (importados a core):
// Palabras de control
keywordControl:
['si', 'if']
['mientras', 'while']
['para', 'for']
['retornar', 'return']
['romper', 'break']
['continuar', 'continue']
// ... más
// Constantes del lenguaje
constantLanguage:
['verdadero', 'true']
['falso', 'false']
['nulo', 'null']
['indefinido', 'undefined']
// Tipos y almacenamiento
storageType:
['const', 'const']
['mut', 'let']
['var', 'var']
['clase', 'class']
['funcion', 'function']
['asincrono', 'async']
// Funciones de soporte
supportFunction:
['consola', 'console']
['Fecha', 'Date']
['Promesa', 'Promise']
// ... más
###📊 Ejemplo Completo de Transformación
Entrada (EsJS):
mut numeros = [1, 2, 3, 4, 5]
mut resultado = numeros
.filtrar(x => x > 2)
.mapear(x => x * 2)
consola.escribir(resultado)
Proceso:
-
EsSucrase (transform): Convierte estructura básica
let numeros = [1, 2, 3, 4, 5] let resultado = numeros... console... -
Plugins: Aplica transformaciones del dominio
filtrar→filtermapear→mapconsola.escribir→console.log
Salida (JavaScript):
let numeros = [1, 2, 3, 4, 5]
let resultado = numeros
.filter(x => x > 2)
.map(x => x * 2)
console.log(resultado)
###🔄 Bidireccionalidad
EsJS puede funcionar en ambas direcciones:
// EsJS → JavaScript
compile(code, { from: 'esjs', to: 'js' })
// JavaScript → EsJS (invirtiendo mapeos)
compile(code, { from: 'js', to: 'esjs' })
Internamente, cuando toEsJS = true, se invierten los mapeos:
invertMap(methods) // { 'filter' → 'filtrar', ... }
###⚡ Por qué EsSucrase en lugar de Babel
- Velocidad: Solo enfocado en EsJS, no en compatibilidad ES5
- Simplicidd: Parser optimizado heredado de Babel
- Mantenimiento: Fork especializado y controlado
- Tamaño: Mucho más pequeño que Babel completo
- Enfoque: Solo transforma lo necesario para EsJS
###🎯 Resumen del Flujo
Código EsJS
↓
┌─────────────────────┐
│ @es-js/compiler │ ← Parse (AST) + Transform + Generate
│ (EsSucrase) │
└─────────────────────┘
↓ (código intermedio)
┌─────────────────────┐
│ Plugins de Putout │ ← Transformaciones AST especializadas
│ (18 plugins) │
└─────────────────────┘
↓
JavaScript válido ✅
Esta arquitectura permite que EsJS sea rápido, modular, extensible y mantenible, mientras proporciona una experiencia de programación completamente en español.
END OF POST
Claude Haiku 4.5