El editor web de Google Apps Script es suficiente para scripts de 50 líneas que automatizan tareas puntuales. Pero cuando tu proyecto crece, ese editor se convierte en tu peor enemigo. No puedes buscar en múltiples archivos simultáneamente, el sistema de versiones es lineal sin posibilidad de crear branches, y el debugging consiste en insertar Logger.log() estratégicamente y rezar para que los logs te den alguna pista útil.
Clasp (Command Line Apps Script Projects) es la herramienta oficial de Google que te saca del editor web y te permite desarrollar desde VS Code. No es un hack ni una solución de terceros, es el CLI oficial que Google usa internamente para sus propios proyectos de Apps Script. El código fuente está en github.com/google/clasp.
Por qué necesitas Clasp si desarrollas en Google Apps Script
La diferencia principal entre el editor web y trabajar con Clasp es que pasas de un entorno limitado a tu stack de desarrollo completo. VS Code analiza tu código en tiempo real y te ofrece autocompletado basado en análisis semántico, no solo snippets predefinidos. Cuando escribes SpreadsheetApp. y pulsas punto, VS Code te muestra todos los métodos disponibles con su documentación inline. En el editor web escribes a ciegas confiando en tu memoria.
El control de versiones cambia radicalmente. Git funciona de manera nativa sobre tu filesystem local, lo que significa que puedes crear branches para features, hacer cherry-picks de commits específicos y usar estrategias de merge avanzadas. El editor web solo guarda versiones lineales con timestamps. Si dos personas editan el mismo script simultáneamente, el último en guardar sobrescribe los cambios del primero sin posibilidad de merge.
Las búsquedas escalables son otro factor determinante. Necesitas cambiar el nombre de una variable que usas en diez archivos diferentes. En VS Code haces Ctrl+Shift+F, escribes el nombre de la variable, ves todas las ocurrencias en todos los archivos, y haces replace all. En el editor web tienes que abrir cada archivo manualmente, buscar uno por uno, y esperar que no se te escape ninguna ocurrencia.
El debugging mejora drásticamente. El editor web te obliga a usar Logger.log() statements y revisar logs manualmente después de cada ejecución. Con Clasp y VS Code puedes configurar breakpoints condicionales haciendo right-click en el número de línea, añadir watch expressions para monitorear variables en tiempo real mientras el código se ejecuta, navegar el call stack completo para entender cómo llegaste a un punto específico, y configurar exception breakpoints que pausan la ejecución automáticamente cuando ocurre un error.
El tooling profesional es la razón por la que equipos serios no pueden trabajar sin Clasp. ESLint hace análisis estático de código que detecta bugs potenciales antes del deployment: variables declaradas pero nunca usadas, comparaciones que siempre devuelven true o false, async/await mal implementados. Prettier formatea tu código automáticamente siguiendo reglas que configuras una vez. Todo el equipo mantiene el mismo estilo sin discusiones sobre tabs vs spaces o dónde poner las llaves.
TypeScript añade type safety en tiempo de desarrollo. El compilador detecta errores como pasar un string donde se espera un número antes de subir el código al servidor. No necesitas ejecutar el script para descubrir que estás llamando un método que no existe o pasando el número incorrecto de argumentos a una función.
El deployment atómico previene estados inconsistentes. Cuando haces clasp push, Clasp sube todos los archivos modificados en una sola operación transaccional. Si la subida de un archivo falla, ningún archivo se actualiza. Esto evita el escenario donde solo algunos archivos se actualizaron, dejando tu proyecto en un estado intermedio roto.
Qué cambió en Clasp 3.x y por qué te afecta
Google rediseñó Clasp en marzo de 2025 siguiendo principios UNIX de "hacer una cosa y hacerla bien". El cambio más significativo fue eliminar el transpilador TypeScript integrado que Clasp 2.x incluía. En la versión anterior, Clasp convertía archivos .ts a .js automáticamente durante el push. Esto sonaba conveniente pero generaba problemas reales en proyectos de producción.
El transpilador interno usaba versiones desactualizadas del compilador TypeScript porque el equipo de Clasp no podía actualizar tan rápido como salían nuevas versiones de TypeScript. Esto significaba que features nuevas del lenguaje no funcionaban o producían errores crípticos. La configuración del tsconfig.json estaba limitada porque Clasp sobrescribía ciertos valores internamente. Y el debugging se volvía complicado porque el stack trace mostraba líneas del JavaScript transpilado, no del TypeScript original que escribiste.
La solución en Clasp 3.x fue separar responsabilidades. Clasp ahora es puramente un CLI para interactuar con la API de Apps Script. La compilación TypeScript es responsabilidad del desarrollador usando tsc directamente. Esto te da control total del proceso de compilación vía tsconfig.json, compatibilidad con cualquier versión de TypeScript que instales, source maps configurables para debugging preciso, y la posibilidad de usar otros transpiladores como Babel o esbuild si tu proyecto lo requiere.
Los comandos también se normalizaron. Clasp 2.x tenía inconsistencias donde clasp open abría el script pero clasp logs --open necesitaba un flag. La versión 3.x estandariza todos los comandos de "abrir" con formato open-*: clasp open-script para abrir el editor, clasp open-web-app para abrir la webapp desplegada, clasp open-logs para abrir Stackdriver logs. Esta consistencia sigue convenciones de CLIs modernos donde cada comando es un verbo explícito que describe exactamente qué hace.
Instalación y configuración inicial
Apps Script ejecuta código con el runtime V8 moderno, pero Clasp es una herramienta Node.js que necesitas instalar localmente. Verifica que tengas Node.js 16 o superior y npm 8 o superior ejecutando node --version y npm --version en tu terminal. Si las versiones son menores, actualiza Node.js desde su sitio oficial.
La instalación de Clasp se hace globalmente con npm install -g @google/clasp@latest. La flag -g instala el paquete en el path global de npm, típicamente /usr/local/lib/node_modules en Unix o %APPDATA%\npm en Windows. Esto hace que el comando clasp esté disponible en cualquier directorio donde abras una terminal.
Después de instalar, verifica con clasp --version que muestre 3.1.1. Si muestra una versión anterior, el problema suele ser el cache de npm. El cache guarda metadata de paquetes que puede quedar desactualizada, causando que npm instale la versión cacheada en lugar de la última del registry. La solución es ejecutar npm cache clean --force, desinstalar con npm uninstall -g @google/clasp, y volver a instalar. El flag --force elimina el cache completamente forzando a npm a descargar metadata fresca.
Activación de la Apps Script API
Este paso causa el noventa por ciento de errores 403 Forbidden que los desarrolladores encuentran al empezar con Clasp. Tienes que ir a https://script.google.com/home/usersettings y activar el toggle "Google Apps Script API". Sin este paso, nada funcionará aunque la instalación haya sido exitosa.
La razón técnica es que cuando haces clasp login obtienes un token OAuth2 con scopes específicos como https://www.googleapis.com/auth/script.projects y https://www.googleapis.com/auth/script.deployments. Pero estos tokens no son suficientes para interactuar con tus proyectos. La Apps Script API es una API REST separada que Google desactivó por defecto por razones de seguridad heredadas de cuando permitía acceso programático sin rate limits.
Al activar el toggle, estás habilitando el endpoint https://script.googleapis.com/v1/projects/{scriptId}. Sin esta activación, Clasp puede autenticarte correctamente, el comando clasp login completa sin errores y guarda tu token, pero cualquier operación que requiera leer o escribir el proyecto como clasp push, clasp pull o clasp create falla con 403 porque el endpoint retorna "API disabled".
Después de activar el toggle, verifica ejecutando clasp list. Si muestra tus proyectos de Apps Script existentes, la API está correctamente habilitada y puedes continuar. Si sigue fallando con 403, revisa que estés usando la cuenta Google correcta y que el toggle realmente esté en la posición "on".
Autenticación y manejo de múltiples cuentas
El comando clasp login inicia un flujo OAuth2 estándar. Clasp levanta un servidor local en http://localhost:8080 y abre tu navegador en la página de autorización de Google. Aceptas los permisos solicitados y Google redirige a localhost:8080 con un código de autorización en la URL. Clasp intercepta ese código y lo intercambia por un access token y un refresh token que guarda en ~/.clasprc.json.
La estructura de ese archivo JSON incluye el access token que expira cada hora, el refresh token que se usa para obtener nuevos access tokens sin requerir reautenticación manual, los scopes autorizados, y el tipo de token que siempre es Bearer. También guarda la configuración del cliente OAuth2 con client ID, client secret, y redirect URI que apunta al servidor local.
Clasp 3.1 introdujo gestión de múltiples perfiles porque es común tener una cuenta personal de Gmail y una cuenta de trabajo de Google Workspace. Cuando ejecutas clasp login --user personal@gmail.com y luego clasp login --user trabajo@empresa.com, Clasp crea archivos separados: ~/.clasprc.json para la cuenta default, ~/.clasprc-personal.json para el perfil "personal", y ~/.clasprc-trabajo.json para el perfil "trabajo".
Puedes especificar qué usuario usar por comando con clasp --user trabajo push o clasp --user personal open-script. Pero el método recomendado para proyectos es añadir el campo user en el .clasp.json del proyecto. Cuando Clasp lee ese archivo y ve "user": "trabajo", usa ese perfil automáticamente para todos los comandos sin necesidad de especificar la flag cada vez.
Creación vs clonación de proyectos
Crear un proyecto nuevo empieza haciendo un directorio, ejecutando npm init -y para generar un package.json básico, y ejecutando clasp create --title "Mi Proyecto" --type standalone. El flag --type acepta varios valores: standalone para scripts independientes sin vinculación a documentos, sheets para scripts vinculados a Google Sheets, y docs, slides, o forms para vinculación a esos tipos de documentos respectivamente.
La diferencia entre container-bound y standalone es importante. Un script container-bound vive literalmente dentro del documento. Tiene acceso directo al documento sin necesidad de solicitar permisos OAuth adicionales porque el contexto de ejecución ya está dentro del documento. Los usuarios acceden al script desde el menú "Extensions → Apps Script" del documento. Un script standalone es independiente, existe fuera de cualquier documento, y necesita obtener permisos OAuth explícitos para acceder a documentos. Se puede desplegar como webapp con su propia URL o como API endpoint.
El comando clasp create genera tres archivos automáticamente: .clasp.json que contiene la configuración local del proyecto incluyendo el script ID, appsscript.json que es el manifiesto del script con metadata como scopes y timezone, y Code.gs que es el archivo inicial de código.
Clonar un proyecto existente se hace con clasp clone seguido del script ID. Encuentras el ID abriendo el proyecto en script.google.com, mirando la URL que tiene formato https://script.google.com/home/projects/1AbC123_TuScriptId_XyZ/edit, y copiando la parte entre /projects/ y /edit. Alternativamente, dentro del proyecto puedes hacer click en el icono de engranaje para abrir Project settings, y copiar el Script ID de la sección "IDs".
Cuando ejecutas clasp clone, Clasp descarga todos los archivos de código (.gs y .html), el appsscript.json, y crea el .clasp.json local con el script ID. Preserva la estructura de carpetas si existían como nombres con barras diagonales. Pero hay elementos que NO se descargan: los triggers configurados en el proyecto, los deployments que son versiones desplegadas con URLs, y los logs históricos. Esos elementos viven en la nube y se manejan con comandos específicos como clasp deploy o clasp logs.
Estructura de carpetas para proyectos escalables
Un proyecto bien estructurado separa código por responsabilidades. El directorio src/ contiene todo el código fuente. Dentro de src/, el archivo Code.js actúa como entry point principal. El subdirectorio config/ guarda archivos como constants.js con IDs de hojas, URLs de APIs, y configuración que no quieres hardcodear en múltiples lugares.
El subdirectorio models/ contiene clases y objetos de negocio. Por ejemplo, una clase User con métodos toJSON() y validate(). El subdirectorio services/ tiene la lógica de negocio: archivos como SheetService.js que encapsulan toda la interacción con SpreadsheetApp, EmailService.js que maneja envío de emails con GmailApp, DriveService.js para operaciones con archivos.
El subdirectorio utils/ contiene funciones puras sin side effects. Son funciones que reciben inputs, ejecutan lógica, y retornan outputs sin modificar estado global ni interactuar con APIs externas. Estas funciones son las más fáciles de testear porque no dependen de contexto. El subdirectorio ui/ contiene solo presentación: templates HTML puros para sidebars y dialogs, y archivos CSS incluidos.
Si usas TypeScript, añades un directorio dist/ al nivel raíz que es donde el compilador tsc genera archivos .js. Clasp sube desde dist/, no desde src/. Esto separa código fuente TypeScript de código ejecutable JavaScript. El directorio tests/ también va al nivel raíz, fuera de src/, porque los tests se ejecutan con frameworks de Node.js como Jest o Mocha localmente, no en el runtime de Apps Script.
El archivo .claspignore funciona como .gitignore pero para Clasp. Listas patrones de archivos que no quieres subir: node_modules/** porque son dependencias locales, .git/** porque es metadata de Git, tests/** porque los tests solo corren localmente, archivos .env con secretos, el README.md, y archivos de configuración como tsconfig.json o .eslintrc.json.
El misterio de .gs vs .js y las carpetas virtuales
Un archivo .gs es JavaScript puro. La extensión es solo una convención de Google, no existe ninguna diferencia semántica en el lenguaje. Apps Script lee el archivo y lo pasa al engine V8 de JavaScript para ejecución. V8 no reconoce extensiones, solo parsea JavaScript según el estándar ECMAScript.
Cuando subes un archivo Code.js con Clasp, en el editor web aparece como Code.gs. Clasp convierte automáticamente .js a .gs durante el upload. Puedes controlar este comportamiento en .clasp.json con el campo scriptExtensions: [".js", ".gs"]. Con esta configuración, los archivos .js locales se mantienen como .js en el servidor sin conversión.
La ventaja de usar .js localmente es que VS Code asocia la extensión con JavaScript inmediatamente sin configuración adicional. IntelliSense funciona out-of-the-box, plugins de linting como ESLint funcionan sin necesidad de configurar asociaciones de archivos. La desventaja es que si trabajas híbrido, a veces editando localmente y a veces en el editor web, puede generar confusión ver .js en un lado y .gs en el otro.
El editor web de Apps Script es completamente plano, no tiene concepto de carpetas o directorios. Pero soporta nombres de archivos con barras diagonales que simulan carpetas visualmente. Si tienes un archivo local en src/services/EmailService.js, después de hacer clasp push no hay una carpeta "services" en el servidor. Hay un archivo llamado literalmente services/EmailService.js donde el nombre completo incluye la barra.
Internamente, Google almacena esto como un array de objetos donde cada objeto tiene un campo name que es un string: {"name": "services/EmailService", "type": "SERVER_JS", "source": "..."}. El UI del editor detecta el carácter / en el nombre y construye la vista de árbol visual, pero en el backend es simplemente un string con barras. Esta implementación tiene una limitación: no puedes tener un archivo services/Email.js y otro archivo services.js porque habría conflicto de nombres.
Configuración .clasp.json definitiva
El campo scriptId es el identificador único del proyecto en Google, es inmutable. Si cambias este valor en .clasp.json, Clasp empezará a apuntar a un proyecto diferente. El campo rootDir especifica qué directorio Clasp considera como raíz al hacer push o pull. Cuando configuras "rootDir": "src", Clasp sube el contenido de src/ a la raíz del proyecto en Google. Sin rootDir, Clasp sube desde el directorio actual, lo que puede incluir accidentalmente node_modules/, tests/, y otros archivos que no quieres en el servidor.
El campo scriptExtensions es un array de extensiones válidas para archivos de código. Clasp solo sube archivos que matcheen estas extensiones. Por defecto es [".gs"], pero lo recomendado es [".js", ".gs"] para tener flexibilidad. El campo htmlExtensions hace lo mismo para templates HTML de UIs, por defecto [".html"].
El campo filePushOrder es crítico cuando hay dependencias entre archivos. Especifica el orden explícito en que Clasp sube archivos. Sin este campo, Clasp sube alfabéticamente, lo que causa errores cuando un archivo usa constantes o funciones definidas en otro archivo que se sube después. Por ejemplo, si Code.js usa una constante SHEET_ID definida en config/constants.js, necesitas que constants.js se suba primero. Si Clasp sube alfabéticamente, Code.js se procesa antes que config/constants.js, y en runtime obtienes el error SHEET_ID is not defined.
El archivo appsscript.json siempre debe ser el primero en filePushOrder porque contiene metadata fundamental del proyecto: los scopes OAuth que el script necesita, el timezone para funciones de fecha, la versión del runtime. Apps Script necesita procesar esta metadata antes de procesar cualquier archivo de código.
JavaScript puro: setup minimalista
Para proyectos pequeños o medianos, equipos sin experiencia en TypeScript, o cuando necesitas prototipado rápido, JavaScript puro es suficiente. La estructura es simple: un directorio src/ con Code.js, archivos adicionales de lógica, y el appsscript.json. El .clasp.json tiene "rootDir": "src" y "scriptExtensions": [".js"].
El workflow es directo. Editas archivos en VS Code, ejecutas clasp push para subir cambios al servidor, y ejecutas clasp open-script para abrir el editor web donde puedes probar las funciones manualmente. El feedback es inmediato: editar, push, probar. No hay paso de compilación intermedio.
Las ventajas son el zero setup y la compatibilidad con cualquier nivel de experiencia. No necesitas entender sistemas de tipos ni configuración de compiladores. Las desventajas son la falta de type checking donde los errores solo se descubren en runtime, autocompletado limitado basado en JSDoc que no siempre es preciso, y refactoring más riesgoso porque renombrar una función puede romper llamadas en otros archivos sin que el editor te avise.
TypeScript: setup profesional
TypeScript se vuelve necesario cuando tu proyecto supera las quinientas líneas, cuando trabajas en equipo, o cuando el código está en producción manejando datos críticos. La instalación requiere dos paquetes de npm: typescript que es el compilador tsc, y @types/google-apps-script que son las definiciones de tipos de todas las APIs de Google.
Los archivos de definiciones de tipos son archivos .d.ts que declaran la firma de funciones, interfaces y namespaces sin contener implementación real. Por ejemplo, el archivo de types de Apps Script declara que SpreadsheetApp.getActiveSpreadsheet() retorna un objeto tipo Spreadsheet, y que ese objeto tiene métodos getName() que retorna string y getSheets() que retorna un array de Sheet. Con estas definiciones, TypeScript puede validar en tiempo de desarrollo que no estás llamando métodos que no existen o pasando argumentos del tipo incorrecto.
El archivo tsconfig.json controla el comportamiento del compilador TypeScript. El campo target: "ES2022" le dice al compilador que genere código compatible con ECMAScript 2022, que incluye features como optional chaining y nullish coalescing que el runtime V8 de Apps Script soporta completamente.
El campo más crítico es module: "None". Apps Script no soporta sistemas de módulos, ni CommonJS ni ES Modules. Todo el código corre en un scope global compartido. Si configuras "module": "CommonJS", TypeScript generaría llamadas a require() que no existen en Apps Script. Si configuras "module": "ES2015" o superior, generaría statements import y export que tampoco funcionan. Con "module": "None", TypeScript no genera ningún código de módulos, solo transpila la sintaxis del lenguaje.
El ejemplo concreto de este problema: escribes dos archivos TypeScript donde utils.ts exporta una función add y Code.ts la importa. Con "module": "CommonJS", el compilador genera exports.add = function(a, b) {...} en utils.js y const { add } = require('./utils') en Code.js. Ese código falla en Apps Script porque no existe exports ni require. Con "module": "None", el compilador genera simplemente function add(a, b) {...} como función global, y en Code.js llamas directamente add(2, 3) porque está en el scope global.
El campo strict: true activa todas las verificaciones estrictas de TypeScript simultáneamente. Esto incluye noImplicitAny que produce error si el compilador infiere el tipo any implícitamente, strictNullChecks que trata null y undefined como tipos distintos previniendo el error clásico "Cannot read property of null", y noImplicitThis que produce error si this tiene tipo any.
El campo types: ["@types/google-apps-script"] limita los types disponibles exclusivamente a los de Apps Script. Esto previene que uses accidentalmente types de Node.js como fs o http que no existen en el runtime de Apps Script.
El .clasp.json para proyectos TypeScript necesita "rootDir": "dist" en lugar de "src". Clasp sube los archivos JavaScript compilados que están en dist/, no el código fuente TypeScript que está en src/. También especificas "scriptExtensions": [".js"] porque los archivos en dist/ son .js.
El workflow TypeScript añade un paso de compilación. Escribes código en src/, ejecutas npm run build que ejecuta tsc y genera archivos .js en dist/, ejecutas clasp push que sube esos archivos compilados, y ejecutas clasp open-script para probar. El package.json puede tener scripts que simplifican esto: un script push que hace npm run build && clasp push combinando ambos pasos, y un script watch que ejecuta tsc --watch & clasp push --watch en paralelo.
El watch mode es el workflow más productivo. tsc --watch escucha cambios en archivos .ts y recompila automáticamente. clasp push --watch escucha cambios en archivos en dist/ y sube automáticamente. El símbolo & ejecuta ambos comandos en paralelo. El resultado es que guardas un archivo Code.ts en VS Code, TypeScript lo detecta y compila a Code.js en menos de un segundo, Clasp detecta el nuevo Code.js y lo sube al servidor en otro segundo. El ciclo completo de guardar archivo a tener el código actualizado en Google toma menos de dos segundos.
appsscript.json: el manifiesto del proyecto
El campo timeZone controla la zona horaria que Apps Script usa para funciones de fecha. Cuando ejecutas new Date(), el objeto Date resultante usa esta zona horaria. El valor debe ser un identificador válido de la base de datos tz como America/Mexico_City o Europe/Madrid.
El campo exceptionLogging controla dónde van los logs de errores. El valor "STACKDRIVER" envía logs a Google Cloud Logging que ofrece interfaz avanzada con filtros, búsquedas, y análisis de patrones. El valor "NONE" desactiva logging automático y solo guardas lo que explícitamente escribes con Logger.log().
El campo runtimeVersion debe ser "V8" para usar el engine moderno con soporte de ES6 y superior. El valor legacy "DEPRECATED_ES5" solo existe por compatibilidad con scripts antiguos y no debería usarse en proyectos nuevos.
El array oauthScopes especifica qué permisos OAuth el script solicitará al usuario. Los scopes comunes incluyen https://www.googleapis.com/auth/spreadsheets para leer y escribir cualquier hoja que el usuario autorice, https://www.googleapis.com/auth/spreadsheets.currentonly para acceder solo a la hoja donde vive el script en caso de container-bound scripts, https://www.googleapis.com/auth/drive.file para acceder solo a archivos creados o abiertos por este script, https://www.googleapis.com/auth/drive para acceder a todo el Drive del usuario, y https://www.googleapis.com/auth/gmail.send para enviar emails.
El principio de mínimo privilegio es crítico aquí. Solo debes solicitar scopes que realmente necesitas. Los usuarios desconfían de scripts que piden acceso completo a Drive cuando solo necesitan leer una hoja específica. Google también rechaza add-ons en el Marketplace que solicitan scopes excesivos sin justificación clara.
El objeto webapp configura deployments de web applications. El campo access puede ser "MYSELF" para que solo tú accedas, "DOMAIN" para limitar a usuarios de tu dominio Google Workspace, "ANYONE" para acceso público pero requiriendo cuenta Google, o "ANYONE_ANONYMOUS" para acceso completamente público sin necesidad de autenticación. El campo executeAs puede ser "USER_ACCESSING" para que el script ejecute con los permisos de quien accede a la webapp, o "USER_DEPLOYING" para que siempre ejecute con los permisos de quien hizo el deployment.
Scripts npm para automatizar todo
El script build simplemente ejecuta tsc para compilar TypeScript. El script push combina compilación y subida con npm run build && clasp push, útil para proyectos TypeScript. El script push:js solo ejecuta clasp push para proyectos JavaScript puro sin compilación.
El script watch es el más potente: tsc --watch & clasp push --watch ejecuta ambos comandos en paralelo. TypeScript recompila automáticamente cuando detecta cambios en archivos fuente, y Clasp sube automáticamente cuando detecta cambios en archivos compilados. El desarrollo se vuelve un flujo continuo donde solo guardas archivos y todo el resto pasa automáticamente.
El script deploy automatiza el proceso completo de crear una versión y desplegarla: npm run build && clasp version "v$(date +%Y%m%d-%H%M)" && clasp deploy. Compila el código, crea una nueva versión con timestamp legible, y hace deployment. Las versiones son snapshots inmutables del código. Los deployments son punteros a versiones específicas que tienen URLs accesibles. Puedes tener diez versiones históricas pero solo dos deployments activos apuntando a versión 8 como "Staging" y versión 10 como "Production".
El script lint ejecuta ESLint sobre tu código para detectar problemas: eslint src/**/*.{js,ts}. ESLint encuentra variables declaradas pero nunca usadas, comparaciones que siempre dan el mismo resultado, funciones async que no usan await, y docenas de otros patrones problemáticos.
El script format ejecuta Prettier para formatear código automáticamente: prettier --write src/**/*.{js,ts,html}. Prettier lee tu configuración en .prettierrc.json y reformatea archivos siguiendo esas reglas. Todo el equipo usa el mismo formatter con la misma configuración, eliminando discusiones sobre estilo de código en code reviews.
Comandos Clasp 3.x esenciales
El comando clasp push sube todos los archivos al servidor. Con el flag --force sobrescribe sin pedir confirmación, útil en scripts automatizados. Con el flag --watch entra en modo vigilancia donde cualquier cambio en archivos locales dispara una subida automática.
El comando clasp pull descarga cambios del servidor a tu directorio local. Si alguien editó el proyecto en el editor web mientras tú trabajabas localmente, pull trae esos cambios. Si hay conflictos porque editaste el mismo archivo localmente, Clasp te advierte y tienes que resolver manualmente como harías con merge conflicts de Git. Puedes especificar --versionNumber 5 para descargar una versión específica del historial.
El comando clasp deploy crea un nuevo deployment. El flag --description añade texto descriptivo que aparece en la lista de deployments. Puedes actualizar un deployment existente pasando su ID con --deploymentId. El comando clasp deployments lista todos los deployments activos del proyecto con sus IDs y URLs. El comando clasp undeploy seguido de un deployment ID elimina ese deployment.
El comando clasp version crea un snapshot inmutable del código actual con el nombre que especifiques. Esto es útil para poder revertir a una versión anterior si introduces bugs en producción. Ejecutas clasp version "v1.0.0", luego haces cambios y descubres un bug crítico, entonces puedes hacer clasp pull --versionNumber del snapshot anterior para volver al estado conocido bueno.
El comando clasp logs muestra los últimos logs en tu terminal. El comando clasp open-logs abre la interfaz web de Google Cloud Logging donde puedes hacer búsquedas avanzadas, filtrar por severity, y analizar patrones en grandes volúmenes de logs.
El comando clasp status muestra qué archivos has modificado localmente que todavía no has subido al servidor. Funciona similar a git status mostrando archivos changed, added, o deleted.
Workflow real de desarrollo día a día
Cada mañana o cuando vuelves al proyecto después de tiempo, ejecutas clasp pull para traer cambios que puedan haber hecho otros miembros del equipo. Luego activas watch mode con npm run watch si usas TypeScript o npm run watch:js si usas JavaScript puro. Esto deja corriendo procesos que vigilan cambios y suben automáticamente.
Editas código en VS Code normalmente. Cada vez que guardas un archivo, los procesos en watch mode detectan el cambio, compilan si es necesario, y suben al servidor. No necesitas ejecutar comandos manualmente. Para probar los cambios, recarga la hoja de cálculo si es un container-bound script, o recarga la webapp si es un web app deployment.
El debugging en Apps Script tiene dos caminos para logs. La función console.log() envía output a Stackdriver (Google Cloud Logging), mientras que Logger.log() envía a Execution log que es más simple pero limitado. Para ver logs de console.log() ejecutas clasp open-logs que abre la interfaz web de Cloud Logging. Para logs de Logger.log() abres el editor web y vas a View → Execution log después de ejecutar la función.
Clasp no soporta breakpoints directos desde VS Code. El workflow de debugging consiste en añadir llamadas a Logger.log() estratégicamente en puntos clave del código, ejecutar la función desde el editor web, y revisar los logs para entender el flujo de ejecución y valores de variables. Para proyectos complejos donde necesitas debugging más avanzado, existen addons experimentales para VS Code como Google Apps Script Debugger, pero requieren configuración adicional.
.claspignore para controlar qué se sube
El archivo .claspignore usa sintaxis de patrones glob idéntica a .gitignore. Listas archivos y directorios que no quieres que Clasp suba al servidor. El directorio node_modules/** contiene dependencias de npm que solo se usan localmente para herramientas como TypeScript o ESLint, no tienen sentido en Apps Script. El directorio .git/** es metadata del sistema de control de versiones que tampoco debe subirse.
El directorio tests/** contiene tests unitarios que ejecutas localmente con frameworks de Node.js. Estos tests no pueden correr en el runtime de Apps Script porque usan APIs de Node.js. Subirlos solo ocuparía cuota del proyecto sin utilidad. Archivos como *.test.js o *.spec.ts son tests individuales que también excluyes por la misma razón.
Archivos .env contienen variables de entorno y a menudo incluyen secrets como API keys. Nunca deben subirse al servidor. El README.md y archivos de configuración como .eslintrc.json, .prettierrc.json, o tsconfig.json son para desarrollo local y no tienen función en el servidor.
Errores comunes y sus soluciones
El error "TypeScript files not supported" ocurre cuando intentas ejecutar clasp push con archivos .ts en Clasp 3.x. La versión 3 eliminó el transpilador integrado, así que debes compilar manualmente. La solución es ejecutar npm run build para compilar TypeScript a JavaScript primero, y luego clasp push sube los archivos compilados. O cambias tu workflow a ejecutar npm run push que hace ambos pasos en un comando.
El error 403 "Insufficient Permission" tiene dos causas principales. La primera es que no activaste la Apps Script API en https://script.google.com/home/usersettings. Ve a esa URL y confirma que el toggle esté activado. La segunda causa es que el token OAuth expiró o se corrompió. La solución es ejecutar clasp logout seguido de clasp login para regenerar tokens frescos.
El error "No files to push" significa que Clasp no encuentra archivos para subir. La causa usual es que el campo rootDir en .clasp.json apunta a un directorio que no existe o está vacío. Ejecuta cat .clasp.json para ver la configuración actual, y ejecuta ls -la src o el directorio que especificaste para verificar que contiene archivos. Ajusta rootDir para que apunte al directorio correcto donde está tu código.
Cuando haces cambios en el editor web y luego editas localmente, los cambios del editor web no aparecen. Esto pasa porque no ejecutaste clasp pull después de editar en web. El workflow correcto es siempre hacer clasp pull antes de editar localmente para sincronizar el estado más reciente del servidor. Si olvidaste hacer pull y ya editaste archivos, tendrás que mergear cambios manualmente comparando versiones.
TypeScript avanzado para código robusto
Las interfaces TypeScript documentan la estructura de objetos que pasas entre funciones. Defines una interface UserData con campos email, name, y lastLogin, y luego declaras funciones que reciben parámetro tipo UserData. TypeScript valida en tiempo de desarrollo que pasas objetos con la estructura correcta, previniendo errores donde olvidas un campo o usas el tipo incorrecto.
Los enums TypeScript agrupan constantes relacionadas. En lugar de tener strings dispersos por tu código con nombres de hojas, defines un enum SheetNames con valores USERS, REPORTS, CONFIG. Cuando necesitas referenciar el nombre de una hoja, usas SheetNames.USERS. Si renombras la hoja física, cambias el valor del enum en un solo lugar y el compilador actualiza todas las referencias automáticamente.
Los generics TypeScript permiten escribir funciones reutilizables que funcionan con múltiples tipos. Una función getColumn que recibe una hoja y un número de columna puede usar generics para retornar array del tipo correcto. Llamas getColumn<string>(sheet, 0) y recibes string[], o llamas getColumn<number>(sheet, 2) y recibes number[]. La misma función, pero type-safe para diferentes tipos de datos.
Los decoradores TypeScript permiten metaprogramación. Puedes escribir un decorador @cache que automáticamente memoriza resultados de funciones costosas. Aplicas el decorador a un método de una clase, y el decorador intercepta llamadas, guarda resultados en un Map usando los argumentos como key, y retorna valores cacheados en llamadas subsecuentes sin ejecutar la función de nuevo.
Siguientes pasos prácticos
El mejor modo de aprender Clasp es crear un proyecto de prueba siguiendo esta guía paso a paso. En treinta minutos tendrás el workflow completo funcionando: instalación de Clasp, autenticación, creación de proyecto, configuración de TypeScript si decides usarlo, y ciclo completo de editar-compilar-subir-probar.
La documentación oficial de Clasp en GitHub contiene referencia completa de todos los comandos con ejemplos. La documentación de Apps Script reference tiene detalles de todas las APIs disponibles con métodos, parámetros, y valores de retorno. El handbook de TypeScript explica todos los features del lenguaje en profundidad si decides usar TypeScript.
Para proyectos que necesitan OAuth2 con servicios externos, la librería apps-script-oauth2 del equipo de Google Workspace simplifica el flujo. Está mantenida activamente y soporta todos los flujos OAuth2 estándar.
El debugging avanzado usando Google Cloud Logging te permite loggear objetos estructurados con console.log({userId: 123, action: 'login', timestamp: new Date()}). En la interfaz de Cloud Logging filtras por campos específicos como jsonPayload.action="login" para encontrar exactamente los logs que necesitas sin revisar miles de líneas manualmente.
Con Clasp configurado correctamente desarrollas diez veces más rápido que en el editor web, produces código mantenible y escalable, debuggeas efectivamente, colaboras fácilmente con Git, y si usas TypeScript obtienes type safety que previene errores antes de deployment. Este es el setup que usan equipos profesionales que mantienen scripts de Apps Script en producción manejando miles de usuarios.