@agus01/node-red-contrib-monaco-editor
v1.1.0
Published
Nodo Node-RED con editor Monaco que incluye validación inteligente de sintaxis para múltiples lenguajes de programación
Downloads
9
Maintainers
Readme
node-red-contrib-monaco-editor
Un nodo Node-RED que integra el Monaco Editor (el mismo editor de VS Code) para edición de código con validación inteligente de sintaxis, soporte multi-lenguaje y autenticación flexible.
Descripción
Este nodo permite crear interfaces de edición de código web completas directamente desde Node-RED, generando páginas HTML dinámicas con un editor Monaco totalmente funcional. Incluye soporte para múltiples lenguajes de programación, validación sintáctica en tiempo real, y diversas opciones de autenticación para interactuar con endpoints remotos o archivos locales.
Características Principales
Editor Monaco Integrado
- Editor de código profesional basado en VS Code
- Tema oscuro optimizado para sesiones largas de codificación
- IntelliSense con autocompletado y sugerencias contextuales
- Minimapa para navegación rápida en archivos grandes
- Ajuste de línea automático para mejor legibilidad
Soporte Multi-Lenguaje
Validación y resaltado de sintaxis para:
- JavaScript (
.js,.mjs,.cjs) - Validación completa con TypeScript compiler - JSON (
.json) - Validación de estructura y sintaxis - HTML (
.html,.htm) - Validación HTML5 completa - CSS (
.css) - Validación de propiedades y sintaxis - XML (
.xml) - Validación de estructura - Markdown (
.md,.markdown) - Resaltado de sintaxis
Validación Inteligente de Sintaxis
JavaScript/TypeScript
- Detección de variables no definidas (errores semánticos)
- Validación de sintaxis ES2020+
- Soporte para módulos ES6+
- Ignorado inteligente de errores de importación no resueltos
- Verificación de tipos con JSDoc (opcional)
HTML
- Validación HTML5 completa
- Detección de etiquetas no cerradas
- Verificación de atributos válidos
- Sugerencias de estructura correcta
JSON
- Validación de sintaxis JSON estricta
- Detección de comas finales o faltantes
- Verificación de estructura de llaves/corchetes
Autenticación Flexible
Cuatro métodos de autenticación soportados:
- Ninguna - Sin autenticación
- Basic Auth - Usuario y contraseña
- Bearer Token - Token de autorización en headers
- Query Token - Token como parámetro en URL
Gestión de Archivos
- Selector de archivos dinámico con carga desde endpoint
- Guardado automático con detección de cambios
- Protección contra pérdida de datos (confirmación antes de cerrar)
- Indicador visual de cambios sin guardar
- Botón de recarga para descartar cambios
Optimización de Rendimiento
- Workers sin CSP blob para cumplir políticas de seguridad
- Carga optimizada desde Node.js local o CDN (fallback)
- Caché de assets con
immutableymaxAge: 1y - Debouncing en detección de cambios (500ms)
Instalación
Desde la paleta de Node-RED
cd ~/.node-red
npm install node-red-contrib-monaco-editorDesde directorio de proyecto
npm install node-red-contrib-monaco-editorDesarrollo/Local
cd ~/.node-red
npm install /ruta/a/node-red-contrib-monaco-editorLuego reinicia Node-RED.
Uso Básico
Configuración del Nodo
- Arrastra el nodo
monaco-editordesde la categoría "function" a tu flujo - Configura las propiedades según tus necesidades:
Propiedades Principales
| Propiedad | Descripción | También vía msg |
|-----------|-------------|-----------------|
| Nombre | Nombre descriptivo del nodo | - |
| Tipo de contenido | Lenguaje del editor (js, json, xml, html, css) | msg.type |
| Endpoint GET | URL para obtener contenido desde servidor | msg.endpointget |
| Endpoint SET | URL para guardar contenido en servidor | msg.endpointset |
| Archivo de contenido | Ruta local al archivo a editar | msg.pathtocontent |
Configuración de Autenticación
| Tipo | Propiedades | También vía msg |
|------|-------------|-----------------|
| Basic Auth | Usuario y Contraseña | msg.basicuser, msg.basicpass |
| Bearer Token | Token de autorización | msg.authtoken |
| Query Token | Token y nombre del parámetro | msg.authtoken, msg.tokenParam |
Ejemplo de Flujo Simple
[
{
"id": "inject1",
"type": "inject",
"name": "Abrir Editor",
"topic": "",
"payload": "// Mi código inicial\nconsole.log('Hola mundo');",
"wires": [["monaco1"]]
},
{
"id": "monaco1",
"type": "monaco-editor",
"name": "Editor JavaScript",
"type": "js",
"endpointget": "http://localhost:1880/api/code/load",
"endpointset": "http://localhost:1880/api/code/save",
"authtype": "none",
"wires": [["http1"]]
},
{
"id": "http1",
"type": "http response",
"name": "Respuesta HTTP",
"wires": []
}
]📘 Ejemplos de Uso
Ejemplo 1: Editor Simple con Contenido Inicial
// Inyectar contenido inicial
msg.payload = "function suma(a, b) {\n return a + b;\n}";
msg.type = "js";
return msg;Ejemplo 2: Editor con Archivo Local
msg.pathtocontent = "/ruta/al/archivo/script.js";
msg.type = "js";
return msg;Ejemplo 3: Editor con API Remota y Basic Auth
msg.endpointget = "https://api.ejemplo.com/code/load";
msg.endpointset = "https://api.ejemplo.com/code/save";
msg.authtype = "basic";
msg.basicuser = "usuario";
msg.basicpass = "contraseña";
msg.type = "json";
return msg;Ejemplo 4: Editor con Bearer Token
msg.endpointget = "https://api.ejemplo.com/files/load";
msg.endpointset = "https://api.ejemplo.com/files/save";
msg.authtype = "bearer";
msg.authtoken = "mi-token-secreto-123";
msg.type = "html";
return msg;Ejemplo 5: Múltiples Archivos desde API
// El editor automáticamente llamará a /api/code/files para listar archivos
msg.endpointget = "http://localhost:1880/api/code/load";
msg.endpointset = "http://localhost:1880/api/code/save";
msg.authtype = "querytoken";
msg.authtoken = "abc123";
msg.tokenParam = "apikey";
msg.type = "js";
return msg;🔌 API de Endpoints
GET - Cargar Contenido
URL: {endpointget}?filename={nombre_archivo}
Headers:
Content-Type: application/json
Authorization: Basic/Bearer (según configuración)Query Params (Query Token):
?token=abc123 (o el nombre personalizado en tokenParam)Respuesta esperada:
{
"content": "contenido del archivo...",
"filename": "script.js"
}POST - Guardar Contenido
URL: {endpointset}?filename={nombre_archivo}
Headers:
Content-Type: application/json
Authorization: Basic/Bearer (según configuración)Body:
{
"content": "nuevo contenido...",
"code": "nuevo contenido...",
"filename": "script.js"
}GET - Listar Archivos
URL: {endpointget sin /load}/files
Ejemplo: Si endpointget es /api/code/load, el endpoint de archivos será /api/code/files
Respuesta esperada:
{
"files": [
{ "name": "script1.js" },
{ "name": "config.json" },
{ "name": "styles.css" }
]
}O formato simplificado:
["script1.js", "config.json", "styles.css"]Configuración Avanzada
Workers de Monaco
El nodo configura automáticamente los workers especializados para cada lenguaje:
- JavaScript/TypeScript:
typescriptWorker.js - HTML:
htmlWorker.js - CSS/SCSS/LESS:
cssWorker.js - JSON:
jsonWorker.js - Editor base:
monaco-worker.js
Los workers se sirven desde:
- Local:
/monaco-vs/vs/*(cuando monaco-editor está instalado) - CDN Fallback:
https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/
Opciones de Validación JavaScript
// Configuración interna del nodo (no modificable sin editar código)
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.ESNext,
allowJs: true,
checkJs: true, // Validación semántica activada
skipLibCheck: true
});Detección Automática de Lenguaje
El editor detecta automáticamente el lenguaje basándose en la extensión del archivo:
// Mapeo interno
.js, .mjs, .cjs → JavaScript
.json → JSON
.html, .htm → HTML
.css → CSS
.xml → XML
.md, .markdown → MarkdownArquitectura Técnica
Estructura del Proyecto
node-red-contrib-monaco-editor/
├── monaco.html # Definición del nodo en Node-RED UI
├── monaco.js # Lógica del nodo en Node.js
├── package.json # Metadata y dependencias
└── README.md # Este archivoFlujo de Datos
Usuario → Inyecta msg → Nodo Monaco → Genera HTML → HTTP Response
↓
Configura opciones
(endpoints, auth, tipo)
↓
Sirve Monaco assets
(workers, VS loader)
↓
Cliente web carga editor
↓
Interactúa con endpoints
(GET/POST archivos)Componentes Clave
1. monaco.html - Configuración UI
- Define la interfaz de configuración del nodo
- Campos dinámicos según tipo de autenticación
- Validación de inputs
- Tooltips y ayuda contextual
2. monaco.js - Lógica del Servidor
- Montaje de assets estáticos: Sirve Monaco desde
node_modules - Generación HTML dinámica: Crea el HTML del editor con configuración embebida
- Sistema de syntax providers: Gestión de lenguajes soportados
- Detección de lenguaje: Por extensión de archivo
- Gestión de workers: Sin uso de
blob:para compatibilidad CSP
3. Editor HTML Generado - Cliente Web
- Interfaz del editor: Monaco embebido con toolbar
- Gestión de estado: Detección de cambios, dirty flag
- Comunicación con endpoints: Fetch API con autenticación
- Selector de archivos: Listado y carga dinámica
- Prevención de pérdida: Confirmación antes de cerrar
Opciones de Configuración del Editor
Opciones Monaco Predefinidas
{
automaticLayout: true, // Ajuste automático al contenedor
minimap: { enabled: true }, // Minimapa visible
theme: 'vs-dark', // Tema oscuro
fontSize: 14, // Tamaño de fuente
lineNumbers: 'on', // Números de línea
wordWrap: 'on', // Ajuste de línea
suggestOnTriggerCharacters: true,
quickSuggestions: {
other: true,
comments: false,
strings: false
}
}Atajos de Teclado
| Atajo | Acción |
|-------|--------|
| Ctrl/Cmd + S | Guardar archivo |
| Ctrl/Cmd + Z | Deshacer |
| Ctrl/Cmd + Y | Rehacer |
| Ctrl/Cmd + F | Buscar |
| Ctrl/Cmd + H | Buscar y reemplazar |
| F1 | Paleta de comandos |
Depuración y Logs
Logs del Servidor (Node-RED)
El nodo emite logs en la consola de Node-RED:
[Monaco Worker] editor (local) baseUrl= /monaco-vs/
[Monaco Worker] json (local) baseUrl= /monaco-vs/
[DEBUG] after setModelLanguage(javascript) { langId: 'javascript', ... }Logs del Cliente (Navegador)
Abre la consola del navegador (F12) para ver:
Monaco Editor Config: { language: 'javascript', endpointGet: '...' }
loadFileList() - Iniciando carga de archivos...
[Markers update][current] { uri: '...', count: 2, markers: [...] }
[HTML markers recargados] [...]Utilidad de Depuración
El editor expone una utilidad global en la consola del navegador:
// Ver estado actual del editor
window.monacoDebug.debugState('mi-tag');
// Ver markers (errores/advertencias)
window.monacoDebug.getCurrentMarkers();
// Ver lenguajes disponibles
window.monacoDebug.getLanguages();Seguridad
Content Security Policy (CSP)
El nodo evita el uso de blob: URLs para los workers, siendo compatible con políticas CSP estrictas:
Content-Security-Policy: script-src 'self' 'unsafe-eval'Recomendaciones de Seguridad
- Usa HTTPS para endpoints remotos
- Almacena tokens en variables de entorno, no en el flujo
- Valida entrada en tus endpoints antes de guardar archivos
- Limita permisos de escritura en el sistema de archivos
- Implementa rate limiting en tus APIs
📊 Mensajes de Entrada y Salida
Mensaje de Entrada (msg)
{
payload: "código inicial", // Contenido inicial del editor
type: "js", // Tipo de contenido (js, json, html, css, xml)
endpointget: "/api/load", // Endpoint GET
endpointset: "/api/save", // Endpoint POST
pathtocontent: "/ruta/file.js", // Archivo local
authtype: "bearer", // Tipo de autenticación
authtoken: "token123", // Token de autenticación
basicuser: "user", // Usuario (basic auth)
basicpass: "pass", // Contraseña (basic auth)
tokenParam: "apikey" // Nombre parámetro token (query)
}Mensaje de Salida (msg)
{
payload: "<html>...</html>", // HTML completo del editor
editorConfig: {
language: "javascript",
endpoints: {
get: "/api/load",
set: "/api/save"
},
authentication: "bearer",
filePath: "/ruta/file.js"
}
}🧪 Testing
Prueba Básica
- Crea un flujo con:
- Nodo
inject→monaco-editor→http response
- Nodo
- Configura un endpoint HTTP GET en el puerto que uses
- Inyecta el mensaje
- Abre el navegador en la URL configurada
Servidor de Prueba
// Ejemplo de servidor Express simple para testing
const express = require('express');
const app = express();
app.use(express.json());
let codeStorage = {
'test.js': 'console.log("Hola mundo");',
'config.json': '{\n "nombre": "prueba"\n}'
};
// Listar archivos
app.get('/api/code/files', (req, res) => {
res.json({
files: Object.keys(codeStorage).map(name => ({ name }))
});
});
// Cargar archivo
app.get('/api/code/load', (req, res) => {
const filename = req.query.filename;
res.json({
content: codeStorage[filename] || '',
filename: filename
});
});
// Guardar archivo
app.post('/api/code/save', (req, res) => {
const { filename, content } = req.body;
codeStorage[filename] = content;
res.json({ success: true });
});
app.listen(1880, () => console.log('Server on port 1880'));Changelog
v1.1.0
- Validación completa de JavaScript con detección de variables no definidas
- Validación HTML5 con detección de etiquetas y atributos
- Sistema de syntax providers extensible
- Soporte para múltiples archivos desde endpoint
- Workers sin blob para compatibilidad CSP
- Debouncing en detección de cambios
- Protección contra pérdida de datos
- Corrección de carga de workers HTML
- Mejoras en detección de lenguaje por extensión
v1.0.0
- Lanzamiento inicial
- Integración de Monaco Editor
- Soporte multi-lenguaje (JS, JSON, HTML, CSS, XML)
- Autenticación flexible (none, basic, bearer, query token)
- Carga/guardado desde endpoints
- Lectura de archivos locales
Licencia
MIT License - Ver archivo LICENSE para más detalles
