
Esta es la segunda parte de una publicación sobre la creación de una tubería que puede tomar un archivo de boceto y exportar todos los íconos incluidos en el archivo, en diferentes formatos, para diferentes plataformas, con la posibilidad de que AB pruebe cada ícono.
Puedes leer la primera parte de la publicación
aquí .

Los archivos de Sketch, con todos los íconos recopilados, con estilo y con el nombre apropiado, estaban listos. Ahora era el momento de comenzar a escribir el código.
Basta decir que el proceso fue en gran medida una prueba y error: después del importante núcleo de código inicial, desarrollado por el líder de mi equipo
Nikhil Verma (quien estableció los fundamentos del guión), pasé por un proceso incremental que requirió al menos tres fases de refactorización y bastantes revisiones. Por esta razón, no entraré en demasiados detalles sobre cómo se desarrolló el script, sino que me centraré en cómo funciona el script hoy, en su forma final.
El script de compilación
El script de compilación, escrito en Node.js, es relativamente sencillo en su flujo: una vez importadas las dependencias, declaró la lista de archivos de Sketch para procesar (como una lista de marcas y para cada marca una lista de archivos para esa marca) y comprobó que Sketch está instalado en el cliente, el script se repite en el conjunto de marcas y para cada una de ellas ejecuta estos pasos en secuencia:
- Obtenga los tokens de diseño para la marca (necesitamos los valores de color)
- Clone los archivos Sketch asociados con la marca, descomprímalos para exponer los archivos JSON internos y manipule algunos de los valores internos de estos archivos JSON (más sobre esto más adelante)
- Lea los metadatos relevantes de los archivos de Sketch JSON ( document.json , meta.json y pages / pageUniqueID.json ); en particular, necesitamos la lista de estilos compartidos y la lista de activos / iconos contenidos en los archivos
- Después de algunas manipulaciones adicionales de los archivos Sketch JSON, vuelva a comprimirlos y, utilizando los archivos Sketch (clonados y actualizados), exporte y genere los archivos de salida finales para las tres plataformas (iOS, Android, Mobile Web)
Puede ver las partes relevantes del script de compilación principal aquí:
// ... modules imports here const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'], }; const SKETCH_FOLDER_PATH = path.resolve(__dirname, '../src/'); const SKETCH_TEMP_PATH = path.resolve(SKETCH_FOLDER_PATH, 'tmp'); const DESTINATION_PATH = path.resolve(__dirname, '../dist'); console.log('Build started...'); if (sketchtool.check()) { console.log(`Processing Sketch file via ${sketchtool.version()}`); build(); } else { console.info('You need Sketch installed to run this script'); process.exit(1); } // ---------------------------------------- function build() { // be sure to start with a blank slate del.sync([SKETCH_TEMP_PATH, DESTINATION_PATH]); // process all the brands declared in the list of Sketch files Object.keys(SKETCH_FILES).forEach(async (brand) => { // get the design tokens for the brand const brandTokens = getDesignTokens(brand); // prepare the Sketch files (unzipped) and get a list of them const sketchUnzipFolders = await prepareSketchFiles({ brand, sketchFileNames: SKETCH_FILES[brand], sketchFolder: SKETCH_FOLDER_PATH, sketchTempFolder: SKETCH_TEMP_PATH }); // get the Sketch metadata const sketchMetadata = getSketchMetadata(sketchUnzipFolders); const sketchDataSharedStyles = sketchMetadata.sharedStyles; const sketchDataAssets = sketchMetadata.assetsMetadata; generateAssetsPDF({ platform: 'ios', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); generateAssetsSVGDynamicMobileWeb({ platform: 'mw', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); generateAssetsVectorDrawableDynamicAndroid({ platform: 'android', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); }); }
En realidad, todo el código de canalización es mucho más complejo que esto, y la complejidad radica en las
funciones prepareSketchFiles ,
getSketchMetadata y
generateAssets [formato] [plataforma] . Trataré de explicarlos con más detalle a continuación.
Preparando los archivos de croquis
El primer paso en el proceso de compilación es la preparación de los archivos de Sketch, de modo que puedan usarse más tarde para la exportación de los activos para las diferentes plataformas.
Los archivos asociados con la marca, por ejemplo, para
Blendr , los archivos
icons_common.sketch y
icons_blendr.sketch , se clonan inicialmente en una carpeta temporal (más precisamente, en una subcarpeta con el nombre de la marca que se está procesando) y se descomprimen.
Luego, se procesan los archivos JSON internos, a un prefijo agregado a los activos que se someterán a pruebas AB, de modo que cuando se exporten, se guardarán en una subcarpeta con un nombre predefinido (el nombre único del experimento). Para comprender qué activos se deben probar, simplemente verificamos si el nombre de la página en la que están almacenados en Sketch tiene el prefijo
"XP_" .
Una comparación de los nombres de las capas, dentro de los archivos de Sketch, antes y después de la actualización.En el ejemplo anterior, cuando se exportan los activos se guardarán en la subcarpeta
"this__is_an_experiment" , con un nombre de archivo
"icon-name [variant-name] .ext" .
Leer los metadatos del boceto
El segundo paso importante en el proceso es sacar todos los metadatos relevantes de los archivos de Sketch, en particular de sus archivos JSON internos. Como se explicó anteriormente, estos archivos son los dos archivos principales (
document.json y
meta.json ) y los archivos de
páginas (
pages / pageUniqueId.json ).
El
archivo document.json se usa para obtener la lista de los estilos compartidos, que aparecen debajo de la propiedad de objeto
layerStyles :
{ "_class": "document", "do_objectID": "45D2DA82-B3F4-49D1-A886-9530678D71DC", "colorSpace": 1, ... "layerStyles": { "_class": "sharedStyleContainer", "objects": [ { "_class": "sharedStyle", "do_objectID": "9BC39AAD-CDE6-4698-8EA5-689C3C942DB4", "name": "features/feature-like", "value": { "_class": "style", "fills": [ { "_class": "fill", "isEnabled": true, "color": { "_class": "color", "alpha": 1, "blue": 0.10588235408067703, "green": 0.4000000059604645, "red": 1 }, "fillType": 0, "noiseIndex": 0, "noiseIntensity": 0, "patternFillType": 1, "patternTileScale": 1 } ], "blur": {...}, "startMarkerType": 0, "endMarkerType": 0, "miterLimit": 10, "windingRule": 1 } }, ...
Para cada estilo, almacenamos información básica en un objeto clave-valor. Esto se usará más adelante cuando necesitemos recuperar el nombre de un estilo basado en su ID única (en Sketch, la propiedad
do_objectID ):
const parsedSharedStyles = {}; parsedDocument.layerStyles.objects.forEach((object) => { parsedSharedStyles[object.do_objectID] = { name: object.name, isFill: _.get(object, 'value.fills[0].color') !== undefined, isBorder: _.get(object, 'value.borders[0].color') !== undefined, }; });
En este punto, nos movemos en el archivo
meta.json para obtener la lista de páginas, en particular necesitamos su
id y
nombre únicos :
{ "commit": "623a23f2c4848acdbb1a38c2689e571eb73eb823", "pagesAndArtboards": { "EE6BE8D9-9FAD-4976-B0D8-AB33D2B5DBB7": { "name": "Icons", "artboards": { "3275987C-CE1B-4369-B789-06366EDA4C98": { "name": "badge-feature-like" }, "C6992142-8439-45E7-A346-FC35FA01440F": { "name": "badge-feature-crush" }, ... "7F58A1C4-D624-40E3-A8C6-6AF15FD0C32D": { "name": "tabbar-livestream" } ... } }, "ACF82F4E-4B92-4BE1-A31C-DDEB2E54D761": { "name": "XP_this__is_an_experiment", "artboards": { "31A812E8-D960-499F-A10F-C2006DDAEB65": { "name": "this__is_an_experiment/tabbar-livestream[variant1]" }, "20F03053-ED77-486B-9770-32E6BA73A0B8": { "name": "this__is_an_experiment/tabbar-livestream[variant2]" }, "801E65A4-3CC6-411B-B097-B1DBD33EC6CC": { "name": "this__is_an_experiment/tabbar-livestream[control]" } } },
Luego, para cada página leemos el archivo JSON correspondiente debajo de la carpeta de
páginas (como ya se dijo, el nombre de archivo es
[pageUniqueId] .json ), y revisamos los activos contenidos en esa página (aparecen como capas). De esta manera, para cada icono obtenemos su nombre, su ancho / alto, los metadatos de Sketch para ese icono de capa, y si está en una página de experimento, el nombre de la prueba AB en cuestión y el nombre de la variante para ese ícono.
Aviso : el objeto "page.json" es muy complejo, por lo que no voy a entrar aquí. Si tiene curiosidad y quiere ver cómo se ve, le sugiero que cree un nuevo archivo Sketch en blanco, agregue algún contenido y guárdelo; luego cambie el nombre de su extensión en zip, descomprímalo y busque uno de los archivos que aparecen en la carpeta "páginas".
Mientras procesamos las mesas de trabajo, también creamos una lista de experimentos (con sus activos correspondientes) que se utilizarán más adelante para determinar qué opciones de icono se usan y para qué experimento, asociando el nombre de las opciones de icono al objeto "base de icono".
Para cada archivo de boceto que se procesa asociado con la marca, producimos un objeto de
metadatos de activos que se ve así:
{ "navigation-bar-edit": { "do_objectID": "86321895-37CE-4B3B-9AA6-6838BEDB0977", ...sketch_artboard_properties, "name": "navigation-bar-edit", "assetname": "navigation-bar-edit", "source": "icons_common", "width": 48, "height": 48 "layers": [ { "do_objectID": "A15FA03C-DEA6-4732-9F85-CA0412A57DF4", "name": "Path", ...sketch_layer_properties, "sharedStyleID": "6A3C0FEE-C8A3-4629-AC48-4FC6005796F5", "style": { ... "fills": [ { "_class": "fill", "isEnabled": true, "color": { "_class": "color", "alpha": 1, "blue": 0.8784313725490196, "green": 0.8784313725490196, "red": 0.8784313725490196 }, } ], "miterLimit": 10, "startMarkerType": 0, "windingRule": 1 }, }, ], ... }, "experiment-name/navigation-bar-edit[variant]": { "do_objectID": "00C0A829-D8ED-4E62-8346-E7EFBC04A7C7", ...sketch_artboard_properties, "name": "experiment-name/navigation-bar-edit[variant]", "assetname": "navigation-bar-edit", "source": "icons_common", "width": 48, "height": 48 ...
Como puede ver, el mismo "icono" (en este caso
, barra de navegación-edición ) puede tener múltiples "activos" asociados, en términos de experimentos. Pero el mismo ícono puede aparecer con el mismo nombre en un segundo archivo de boceto asociado con la marca, y esto es muy útil: es el truco que hemos usado para compilar un conjunto común de íconos y luego definir diferentes variantes de íconos específicos dependiendo de marca
Es por eso que declaramos los archivos de Sketch asociados con cada marca en particular como una matriz:
const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'], };
Porque en este caso el orden importa. Y, de hecho, en la función
getSketchMetadata , llamada por el script de compilación, no devolvemos los objetos
assetsMetadata (uno por archivo) como una lista, sino que hacemos una fusión profunda de cada objeto, uno en el otro, y luego devuelve un único objeto
activoMetadata combinado.
Esto no es más que la fusión "lógica" de los archivos de Sketch, y sus activos, en un solo archivo. Pero la lógica no es tan simple como parece. Aquí está el esquema que tuvimos que crear para descubrir qué sucede cuando hay íconos con el mismo nombre (posiblemente bajo pruebas AB) en diferentes archivos asociados con la misma marca:
El esquema lógico de cómo funciona la "anulación" del mismo ícono, entre un conjunto común / compartido de íconos e íconos diseñados específicamente para etiquetas blancas (también considerando el caso de las pruebas AB)Generando los archivos finales en diferentes formatos para diferentes plataformas
El último paso del proceso es la generación real de los archivos de iconos con diferentes formatos para las diferentes plataformas (PDF para iOS, SVG / JSX para Web y VectorDrawable para Android).
Como puede ver en la cantidad de parámetros pasados a las funciones
generateAssets [formato] [plataforma] esta es la parte más compleja de la tubería. Aquí es donde
el proceso comienza a dividirse y divergir para las diferentes plataformas. Vea a continuación el flujo lógico completo del script y cómo la parte relacionada con la generación de los activos se
divide en tres flujos similares pero no idénticos:
Para generar los activos finales con los colores correctos asociados con la marca que se está procesando, necesitamos hacer otro conjunto de manipulaciones en los archivos Sketch JSON: iteramos iterativamente sobre cada capa que tiene un estilo compartido aplicado, y reemplazamos el valores de color con los colores de las fichas de diseño de la marca.
Para la generación de Android, se requiere una manipulación adicional (más sobre esto más adelante): cambiamos la propiedad de regla de relleno de cada capa de
par a
impar a
distinto de cero (esto está controlado por la propiedad "windingRule" en el objeto JSON, donde " 1 "significa" par-impar "y" 0 "significa" distinto de cero ").
Una vez completadas estas manipulaciones, comprimimos los archivos de Sketch JSON nuevamente en un archivo de Sketch estándar, de modo que pueda procesarse para exportar los activos con las propiedades actualizadas (los archivos clonados y actualizados son archivos de Sketch absolutamente normales: se pueden abrir en Sketch , visto, editado, guardado, etc.).
En este punto, podemos usar sketchtool (
en un contenedor de nodo ) para exportar automáticamente todos los activos en formatos específicos para plataformas específicas. Para cada archivo asociado con una marca (más correctamente, su versión clonada y actualizada) ejecutamos este comando:
sketchtool.run(`export slices ${cloneSketchFile} --formats=svg <i>--scales=1 </i>--output=${destinationFolder} --overwriting`);
Como puede suponer, este comando exporta los activos en un formato específico, aplicando una escala opcional (por ahora siempre mantenemos la escala original), en una carpeta de destino. La opción de
sobrescritura es clave aquí: de la misma manera que hacemos una "fusión profunda" de los objetos de metadatos de activos (que equivale a una "fusión lógica" de los archivos de Sketch), cuando exportamos lo hacemos desde múltiples archivos a la misma carpeta (única por marca / plataforma). Esto significa que si un activo, identificado por su nombre de capa, ya existía en un archivo Sketch anterior, se sobrescribirá en la siguiente exportación. Lo cual, de nuevo, no es más que una operación de "fusión".
Sin embargo, en este caso, podemos tener algunos activos que son "fantasmas". Esto sucede cuando un icono se prueba con AB en un archivo, pero se sobrescribe en un archivo posterior. En tales casos, los archivos de variantes se exportan a la carpeta de destino, a los que se hace referencia en el objeto
assetsMetadata como activo (con su clave y propiedades), pero no se asocian a ningún activo "base" (debido a la fusión profunda de los objetos
activosMetadata ). Estos archivos se eliminarán en un paso posterior, antes de completar el proceso.
Como se mencionó anteriormente, necesitamos diferentes formatos finales para diferentes plataformas. Para iOS queremos archivos PDF, y podemos exportarlos directamente con el comando
sketchtool . Mientras, para Mobile Web queremos archivos JSX, y para Android queremos archivos VectorDrawable; Por esta razón, exportamos los activos en formato SVG a una carpeta intermedia y luego los sometemos a un procesamiento adicional.
Archivos PDF para iOS
Curiosamente, PDF es el formato (¿solo?) Compatible con Xcode y OS / iOS para importar y representar activos vectoriales (
aquí hay una breve explicación de las razones técnicas detrás de esta elección de Apple).
Como podemos exportar directamente en PDF a través de Sketchtool, no hay necesidad de pasos adicionales para esta plataforma: simplemente guardamos los archivos directamente en la carpeta de destino, y eso es todo.
Reaccionar / archivos JSX para web
En el caso de la Web, utilizamos una biblioteca de nodos llamada
svgr que convierte archivos SVG simples en componentes React. Pero queremos hacer algo aún más poderoso: queremos "pintar dinámicamente" el ícono en tiempo de ejecución, con los colores provenientes de los tokens de diseño. Por esta razón, justo antes de la conversión, reemplazamos en el SVG los valores de
relleno de las rutas que originalmente tenían un estilo compartido aplicado, con el valor de token correspondiente asociado con ese estilo.
Entonces, si este es el archivo
badge-feature-like.svg exportado desde Sketch:
<?xml version="1.0" encoding="UTF-8"?> <svg width="128px" height="128px" viewBox="0 0 128 128" version="1.1" xmlns="<a href="http://www.w3.org/2000/svg">http://www.w3.org/2000/svg</a>" xmlns:xlink="<a href="http://www.w3.org/1999/xlink">http://www.w3.org/1999/xlink</a>"> <!-- Generator: sketchtool 52.2 (67145) - <a href="http://www.bohemiancoding.com/sketch">http://www.bohemiancoding.com/sketch</a> --> <title>badge-feature-like</title> <desc>Created with sketchtool.</desc> <g id="Icons" fill="none" fill-rule="evenodd"> <g id="badge-feature-like"> <circle id="circle" fill="#E71032" cx="64" cy="64" r="64"> <path id="Shape" fill="#FFFFFF" d="M80.4061668,..."></path> </g> </g> </svg>
el activo / icono final de
badge-feature-like.js se verá así:
/* This file is generated automatically - DO NOT EDIT */ /* eslint-disable max-lines,max-len,camelcase */ const React = require('react'); module.exports = function badge_feature_like({ tokens }) { return ( <svg data-origin="pipeline" viewBox="0 0 128 128"> <g fill="none" fillRule="evenodd"> <circle fill={tokens.TOKEN_COLOR_FEATURE_LIKED_YOU} cx={64} cy={64} r={64} /> <path fill="#FFF" d="M80.4061668,..." /> </g> </svg> ); };
Como puede ver, hemos reemplazado el valor estático para el color de
relleno del círculo, por uno dinámico, que toma su valor de los tokens de diseño (estos estarán disponibles para el componente Reaccionar
<Icono /> a través de la API de contexto, Pero esa es otra historia).
Este reemplazo es posible a través de los metadatos de Sketch para el activo almacenado en el objeto
Metadata de los
activos : recorriendo recursivamente las capas del activo, es posible crear un selector DOM (en el caso anterior, sería
#Icons # badge-feature- como #circle ) y
úselo para encontrar el nodo en el árbol SVG y reemplace el valor de su atributo de
relleno (para esta operación usamos la biblioteca
cheerio ).
Archivos VectorDrawable para Android
Android admite gráficos vectoriales utilizando su formato vectorial personalizado, llamado
VectorDrawable . Por lo general, los desarrolladores realizan la conversión de SVG a VectorDrawable
directamente en Android Studio . Pero aquí queríamos automatizar todo el proceso, por lo que necesitábamos encontrar una forma de convertirlos mediante código.
Después de mirar diferentes bibliotecas y herramientas, decidimos usar una biblioteca llamada
svg2vectordrawable . No solo se mantiene activamente (al menos, mejor que los otros que encontramos) sino que también es más completo.
El hecho es que VectorDrawable no está en paridad de características con SVG: algunas de las características avanzadas de SVG (p. Ej., Gradientes radiales, máscaras complejas, etc.)
no son
compatibles , y algunas de ellas han obtenido soporte recientemente (con Android API 24 y más alto). Una desventaja de esto es que en Android pre-24
la regla de relleno "par-impar" no es compatible . Pero en Badoo necesitamos ser compatibles con Android 5 y superior. Es por eso que, como se explicó anteriormente, para Android necesitamos convertir cada ruta en los archivos de Sketch a relleno "distinto de cero".
Potencialmente, los diseñadores podrían hacer esto manualmente:

pero esto puede pasarse por alto fácilmente y, por lo tanto, ser propenso a errores humanos.
Por esta razón, hemos agregado un paso adicional en nuestro proceso para Android, donde convertimos automáticamente todas las rutas a
no cero en el Sketch JSON. Esto es para que cuando exportamos los íconos a SVG, ya estén en este formato, y cada VectorDrawable generado también sea compatible con dispositivos Android 5.
El archivo final
badge-feature-like.xml en este caso tiene este aspecto:
<!-- This file is generated automatically - DO NOT EDIT --> <vector xmlns:android="<a href="http://schemas.android.com/apk/res/android">http://schemas.android.com/apk/res/android</a>" android:width="128dp" android:height="128dp" android:viewportWidth="128" android:viewportHeight="128"> <path android:fillColor="?color_feature_liked_you" android:pathData="M64 1a63 63 0 1 0 0 126A63 63 0 1 0 64 1z" /> <path android:fillColor="#FFFFFF" android:pathData="M80.406 ..." /> </vector>
Como puede ver, también en los archivos de VectorDrawable inyectamos nombres de variables para los colores de
relleno , que están asociados a los tokens de diseño a través de estilos personalizados en las aplicaciones de Android.
Así es como se ve VectorDrawable una vez importado en Android Studio:
Un ejemplo de un icono de VectorDrawable importado a Android StudioUna cosa a tener en cuenta en este caso: Android Studio tiene una forma muy estricta y prescriptiva de organizar los activos: ¡no hay carpetas anidadas y todos los nombres en minúsculas! Esto significaba que teníamos que encontrar un formato ligeramente diferente para los nombres de sus íconos: en el caso de un activo en experimentación, su nombre será algo así como
ic_icon-name__experiment-name__variant-name .
Diccionario JSON como biblioteca de activos
Una vez que los archivos de activos se guardan en su formato final, lo último que queda por hacer es guardar toda la metainformación recopilada durante el proceso de compilación y almacenarla en un "diccionario" para que pueda estar disponible más adelante. cuando los activos son importados y consumidos por la base de código de las diferentes plataformas.
Una vez extraída la lista plana de iconos del objeto
assetsMetadata , la
recorrimos y para cada elemento verificamos:
- si es un activo normal (por ejemplo, tabbar-livestream ), y si lo es, simplemente lo conservamos;
- si se trata de una variante en una prueba AB (p. ej. experimento / tabbar-livestream [variante] ) asociamos su nombre, ruta, prueba AB y nombres de variantes a las propiedades abtest del activo "base" (en este caso, tabbar- livestream ), y luego eliminamos la entrada variante de la lista / objeto (solo cuenta la "base");
- si es una variante "fantasma", eliminamos el archivo y luego eliminamos la entrada de la lista / objeto.
Una vez que se completa el ciclo, el diccionario contendrá la lista de todos y solo los íconos "base" (y sus pruebas AB, si están bajo experimento). Para cada uno de estos, contendrá su nombre, tamaño, ruta y, en caso de que un icono esté bajo prueba AB, la información sobre las diferentes opciones del activo.
Este diccionario se guarda en formato JSON en la carpeta de destino de la
marca y la
plataforma . Aquí, por ejemplo, está el archivo
assets.json generado para la aplicación "Blendr" en "web móvil":
{ "platform": "mw", "brand": "blendr", "assets": { "badge-feature-like": { "assetname": "badge-feature-like", "path": "assets/badge-feature-like.jsx", "width": 64, "height": 64, "source": "icons_common" }, "navigation-bar-edit": { "assetname": "navigation-bar-edit", "path": "assets/navigation-bar-edit.jsx", "width": 48, "height": 48, "source": "icons_common" }, "tabbar-livestream": { "assetname": "tabbar-livestream", "path": "assets/tabbar-livestream.jsx", "width": 128, "height": 128, "source": "icons_blendr", "abtest": { "this__is_an_experiment": { "control": "assets/this__is_an_experiment/tabbar-livestream__control.jsx", "variant1": "assets/this__is_an_experiment/tabbar-livestream__variant1.jsx", "variant2": "assets/this__is_an_experiment/tabbar-livestream__variant2.jsx" }, "a_second-experiment": { "control": "assets/a_second-experiment/tabbar-livestream__control.jsx", "variantA": "assets/a_second-experiment/tabbar-livestream__variantA.jsx" } } }, ... } }
El último paso es comprimir todas las carpetas de
activos . archivos
zip , para que puedan descargarse más fácilmente.
El resultado final
El proceso descrito anteriormente, desde la clonación inicial y la manipulación de los archivos de Sketch, hasta la exportación (y conversión) de los activos en el formato deseado para cada plataforma compatible, hasta el almacenamiento de la metainformación recopilada en una biblioteca de activos. repetido para cada marca declarada en el script de compilación.
A continuación se muestra una captura de pantalla de la estructura de las carpetas
src y
dist , una vez que se completa el proceso de compilación:
Estructura de las carpetas "src" y "dist" después de completar el proceso de compilación.En este punto, con un simple comando es posible cargar todos los recursos (archivos JSON, archivos ZIP y archivos de activos) en un repositorio remoto, y ponerlos a disposición de todas las plataformas, para descargar y consumir en sus bases de código.
(La forma en que las plataformas reales recuperan y procesan los activos, a través de scripts personalizados que se crearon ad-hoc para este propósito, está más allá del alcance de este artículo. Pero esto probablemente se cubrirá muy pronto en otras publicaciones de blog dedicadas, por uno de los otros desarrolladores que trabajaron conmigo en este proyecto).
Conclusiones (y lecciones aprendidas en el camino)
Siempre me ha encantado
Sketch . Durante años ha sido la herramienta de elección "de facto" para el diseño (y desarrollo) de aplicaciones y web. Así que estaba muy interesado y curioso por explorar posibles integraciones como
html-sketchapp o herramientas similares, que podríamos usar en nuestros flujos de trabajo y tuberías.
Este flujo (ideal) siempre ha sido el
santo grial para mí (
y para muchos otros ):

Sketch como herramienta de diseño se puede imaginar como un posible "objetivo" de la base de código.
Pero debo admitir que recientemente comencé a preguntarme si Sketch todavía era la herramienta adecuada, especialmente en el contexto de un Sistema de diseño. Entonces, comencé a explorar nuevas herramientas como
Figma , con sus API abiertas, y
Framer X , con su increíble integración con React, porque no veía esfuerzos equivalentes de Sketch para avanzar hacia la integración con el código (cualquiera que sea el código).
Bueno, este proyecto cambió de opinión. No completamente, pero definitivamente mucho.
Quizás Sketch no está exponiendo oficialmente sus API, pero ciertamente la forma en que han construido la estructura interna de sus archivos es una especie de API "no oficial". Podrían haber usado nombres crípticos u ofuscar las claves en los objetos JSON; en su lugar, han optado por una convención de nomenclatura semántica clara, fácil de leer y legible para humanos. No puedo pensar que esto sea simplemente accidental.
El hecho de que los archivos de Sketch se puedan manipular me ha abierto la mente a una amplia gama de posibles desarrollos y mejoras futuros. Desde complementos para validar el nombre, el estilo y la estructura de las capas para los iconos, hasta posibles integraciones con nuestra wiki y nuestra documentación del sistema de diseño (en ambas direcciones), a través de la creación de aplicaciones Node alojadas en
Electron o
Carlo para facilitar muchas de las tareas repetitivas que los diseñadores deben emprender.
Una ventaja inesperada de este proyecto (al menos para mí) es que ahora los archivos de Sketch con los "iconos Cosmos" se han convertido en una "fuente de verdad", de manera similar a lo que sucedió con el
sistema de diseño Cosmos . Si un icono no está allí, no existe en la base de código (o mejor, no debería existir: pero al menos sabemos que es una excepción). Sé que parece obvio ahora, pero no antes, al menos para mí.
Lo que comenzó como un proyecto MVP, pronto se convirtió en una inmersión profunda (literalmente) en lo interno de los archivos de Sketch, con la conciencia de que estos pueden ser manipulados. Todavía no sabemos a dónde llevará todo esto, pero hasta ahora ha sido un éxito. Los diseñadores, desarrolladores, PM y partes interesadas, todos están de acuerdo en que esto ahorrará mucho trabajo manual para todos y evitará muchos errores potenciales. Pero también abrirá las puertas a los usos de los iconos que han sido imposibles hasta ahora.
Una última cosa: lo que describí en esta larga publicación es una tubería que hemos construido aquí para resolver
nuestros problemas particulares, por lo que necesariamente está súper personalizada para
nuestro contexto. Tenga en cuenta que puede que no se adapte a las necesidades de
su negocio o que sea apropiado para
su contexto.
Pero lo que es importante para mí, y lo que quería compartir, es que se puede hacer. Tal vez de diferentes maneras, con diferentes enfoques y diferentes formatos de salida, que tal vez impliquen menos complejidad (es decir, es posible que no necesite la marca múltiple y la prueba AB). Pero ahora puede automatizar el flujo de trabajo involucrado en la entrega de sus iconos con un script Node.js personalizado y Sketch.
Encuentra tu propia forma de hacerlo. Es divertido (y relativamente fácil).
Créditos
Este gran proyecto fue desarrollado en colaboración con
Nikhil Verma (Mobile Web), quien creó la primera versión del script de compilación, y
Artem Rudoi (Android) e
Igor Savelev (iOS), quienes desarrollaron los scripts que importan y consumen los activos en su respectivas plataformas nativas. Gracias, amigos, fue una maravilla trabajar con ustedes en este proyecto y verlo cobrar vida.