
Il s'agit de la deuxième partie d'un article sur la création d'un pipeline qui peut prendre un fichier Sketch et exporter toutes les icônes incluses dans le fichier, dans différents formats, pour différentes plates-formes, avec la possibilité de tester AB chaque icône.
Vous pouvez lire la première partie de l'article
ici .

Les fichiers Sketch, avec toutes les icônes collectées, stylisées et correctement nommées, étaient prêts. Il était maintenant temps de commencer à écrire le code.
Il suffit de dire que le processus a été un essai et une erreur: après l'important noyau de code initial, développé par mon chef d'équipe
Nikhil Verma (qui a défini les bases du script), j'ai suivi un processus incrémentiel qui a nécessité au moins trois phases de refactoring. et pas mal de révisions. Pour cette raison, je n'entrerai pas dans trop de détails sur la façon dont le script a été développé, mais je me concentrerai plutôt sur la façon dont le script fonctionne aujourd'hui, dans sa forme finale.
Le script de construction
Le script de construction - écrit dans Node.js - est relativement simple dans son flux: une fois les dépendances importées, a déclaré la liste des fichiers Sketch à traiter (comme une liste de marques, et pour chaque marque une liste de fichiers pour cette marque) et vérifié que Sketch est installé sur le client, le script boucle sur le tableau des marques, et pour chacune d'entre elles, il exécute ces étapes dans l'ordre:
- Obtenez les jetons de conception pour la marque (nous avons besoin des valeurs de couleur)
- Clonez les fichiers Sketch associés à la marque, décompressez-les pour exposer les fichiers JSON internes et manipulez certaines des valeurs internes de ces fichiers JSON (plus d'informations à ce sujet plus tard)
- Lire les métadonnées pertinentes des fichiers Sketch JSON ( document.json , meta.json et pages / pageUniqueID.json ); en particulier nous avons besoin de la liste des styles partagés et de la liste des actifs / icônes contenus dans les fichiers
- Après quelques manipulations supplémentaires des fichiers Sketch JSON, zippez-les en arrière et, à l'aide des fichiers Sketch (clonés et mis à jour), exportez et générez les fichiers de sortie finaux pour les trois plates-formes (iOS, Android, Web mobile)
Vous pouvez afficher les parties pertinentes du script de build principal ici:
// ... 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 fait, tout le code du pipeline est beaucoup plus complexe que cela, et la complexité réside dans les fonctions
prepareSketchFiles ,
getSketchMetadata et
generateAssets [format] [platform] . Je vais essayer de les expliquer plus en détail ci-dessous.
Préparation des fichiers d'esquisse
La première étape du processus de construction est la préparation des fichiers Sketch, afin qu'ils puissent être utilisés ultérieurement pour l'exportation des actifs pour les différentes plates-formes.
Les fichiers associés à la marque - pour Blendr, par exemple, les fichiers
icons_common.sketch et
icons_blendr.sketch - sont initialement clonés dans un dossier temporaire (plus précisément, dans un sous-dossier nommé d'après la marque en cours de traitement) et décompressés.
Ensuite, les fichiers JSON internes sont traités, dans un préfixe ajouté aux actifs qui doivent subir des tests AB, de sorte que lorsqu'ils sont exportés, ils seront enregistrés dans un sous-dossier avec un nom prédéfini (le nom unique de l'expérience). Pour comprendre quels actifs sont à tester, nous vérifions simplement si le nom de la page dans laquelle ils sont stockés dans Sketch est préfixé avec
"XP_" .
Une comparaison des noms de calque, à l'intérieur des fichiers Sketch, avant et après la mise à jour.Dans l'exemple ci-dessus, lors de l'exportation, les actifs seront enregistrés dans le sous-dossier
"this__is_an_experiment" , avec un nom de fichier
"icon-name [variant-name] .ext" .
Lecture des métadonnées de l'esquisse
La deuxième étape importante du processus consiste à extraire toutes les métadonnées pertinentes des fichiers Sketch, en particulier de leurs fichiers JSON internes. Comme expliqué ci-dessus, ces fichiers sont les deux fichiers principaux (
document.json et
meta.json ) et les fichiers de
pages (
pages / pageUniqueId.json ).
Le
fichier document.json est utilisé pour obtenir la liste des styles partagés, qui apparaissent sous la propriété d'objet
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 } }, ...
Pour chaque style, nous stockons des informations de base dans un objet valeur-clé. Cela sera utilisé plus tard chaque fois que nous aurons besoin de récupérer le nom d'un style en fonction de son ID unique (dans Sketch, la propriété
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, }; });
À ce stade, nous nous déplaçons sur le fichier
meta.json pour obtenir la liste des pages, en particulier nous avons besoin de leur
identifiant unique et de leur
nom :
{ "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]" } } },
Ensuite, pour chaque page, nous lisons le fichier JSON correspondant sous le dossier des
pages (comme déjà dit, le nom du fichier est
[pageUniqueId] .json ), et nous passons en revue les actifs contenus dans cette page (ils apparaissent sous forme de couches). De cette façon, pour chaque icône, nous obtenons son nom, sa largeur / hauteur, les métadonnées Sketch pour cette icône de couche, et si elle se trouve sur une page d'expérience, le nom du test AB en question et le nom de la variante pour cette icône.
Remarque : l'objet "page.json" est très complexe, donc je ne vais pas y entrer ici. Si vous êtes curieux et que vous voulez voir à quoi il ressemble, je vous suggère de créer un nouveau fichier Sketch vierge, d'y ajouter du contenu et de l'enregistrer; puis renommez son extension en zip, décompressez-la et regardez dans l'un des fichiers qui apparaissent sous le dossier "pages".
Lors du traitement des plans de travail, nous créons également une liste d'expériences (avec leurs actifs correspondants) qui seront utilisées ultérieurement pour déterminer quelles options d'icône sont utilisées et pour quelle expérience, en associant le nom des options d'icône à l'objet "base d'icônes".
Pour chaque fichier Sketch en cours de traitement associé à la marque, nous produisons un objet
assetsMetadata qui ressemble à ceci:
{ "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 ...
Comme vous pouvez le voir, la même "icône" (dans ce cas
-ci la barre de navigation-édition ) peut être associée à plusieurs "actifs", en terme d'expériences. Mais la même icône peut apparaître avec le même nom dans un deuxième fichier Sketch associé à la marque, et cela est très utile: c'est l'astuce que nous avons utilisée, pour compiler un ensemble commun d'icônes, puis définir différentes variantes d'icônes spécifiques en fonction de la marque.
C'est pourquoi nous avons déclaré les fichiers Sketch associés à chaque marque particulière sous forme de tableau:
const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'], };
Parce que dans ce cas, l'ordre est important. Et en fait, dans la fonction
getSketchMetadata , appelée par le script de construction, nous ne
renvoyons pas les objets
assetsMetadata (un par fichier) sous forme de liste, mais faisons plutôt une fusion profonde de chaque objet, l'un dans l'autre, puis nous renvoyer un seul objet
assetsMetadata fusionné.
Ce n'est rien de plus que la fusion "logique" des fichiers Sketch et de leurs ressources en un seul fichier. Mais la logique n'est pas aussi simple qu'il y paraît. Voici le schéma que nous avons dû créer pour comprendre ce qui se passe quand il y a des icônes avec le même nom (éventuellement sous test AB) dans différents fichiers associés à la même marque:
Le schéma logique du fonctionnement de la "substitution" de la même icône, entre un ensemble commun / partagé d'icônes et des icônes spécialement conçues pour les étiquettes blanches (en considérant également le cas des tests AB)Génération des fichiers finaux dans différents formats pour différentes plates-formes
La dernière étape du processus est la génération réelle des fichiers d'icônes avec différents formats pour les différentes plates-formes (PDF pour iOS, SVG / JSX pour Web et VectorDrawable pour Android).
Comme vous pouvez le voir d'après le nombre de paramètres transmis aux fonctions
generateAssets [format] [plateforme], c'est la partie la plus complexe du pipeline. C'est là que
le processus commence à se diviser et à diverger pour les différentes plates-formes. Voir ci-dessous le flux logique complet du script, et comment la partie liée à la génération des actifs
se divise en trois flux similaires mais non identiques:
Afin de générer les actifs finaux avec les couleurs correctes associées à la marque en cours de traitement, nous devons effectuer un autre ensemble de manipulations sur les fichiers JSON Sketch: nous effectuons une boucle itérative sur chaque calque auquel un style partagé est appliqué et remplaçons le valeurs de couleur avec les couleurs des jetons de conception pour la marque.
Pour la génération Android, une manipulation supplémentaire est requise (plus d'informations à ce sujet plus tard): nous changeons la propriété de règle de remplissage de chaque couche de
pair-impair à
non nulle (ceci est contrôlé par la propriété "windingRule" dans l'objet JSON, où " 1 "signifie" pair-impair "et" 0 "signifie" non nul ").
Une fois ces manipulations terminées, nous compressons à nouveau les fichiers Sketch JSON dans un fichier Sketch standard, afin qu'il puisse être traité pour exporter les actifs avec les propriétés mises à jour (les fichiers clonés et mis à jour sont des fichiers Sketch absolument normaux: ils peuvent être ouverts dans Sketch , visualisé, édité, enregistré, etc.).
À ce stade, nous pouvons utiliser sketchtool (
dans un wrapper de nœud ) pour exporter automatiquement tous les actifs dans des formats spécifiques pour des plates-formes spécifiques. Pour chaque fichier associé à une marque (plus correctement, sa version clonée et mise à jour) nous exécutons cette commande:
sketchtool.run(`export slices ${cloneSketchFile} --formats=svg <i>--scales=1 </i>--output=${destinationFolder} --overwriting`);
Comme vous pouvez le deviner, cette commande exporte les actifs dans un format spécifique, en appliquant une mise à l'échelle facultative (pour l'instant, nous conservons toujours l'échelle d'origine) dans un dossier de destination. L'option
--overwriting est clé ici: de la même manière que nous faisons une "fusion profonde" des objets assetsMetadata (ce qui équivaut à une "fusion logique" des fichiers Sketch), lorsque nous exportons, nous le faisons à partir de plusieurs fichiers dans le même dossier (unique par marque / plateforme). Cela signifie que si un élément - identifié par son nom de couche - existait déjà dans un fichier Sketch précédent, il sera remplacé par l'exportation suivante. Ce qui, encore une fois, n'est rien d'autre qu'une opération de «fusion».
Dans ce cas, cependant, nous pouvons avoir certains actifs qui sont des "fantômes". Cela se produit lorsqu'une icône est testée AB dans un fichier, mais remplacée dans un fichier suivant. Dans de tels cas, les fichiers de variantes sont exportés dans le dossier de destination, référencés dans l'objet
assetsMetadata en tant qu'actif (avec sa clé et ses propriétés), mais ne sont associés à aucun actif "de base" (en raison de la fusion profonde des objets
assetsMetadata ). Ces fichiers seront supprimés dans une étape ultérieure, avant la fin du processus.
Comme mentionné ci-dessus, nous avons besoin de différents formats finaux pour différentes plates-formes. Pour iOS, nous voulons des fichiers PDF et nous pouvons les exporter directement avec la commande
sketchtool . Alors que pour Mobile Web, nous voulons des fichiers JSX et pour Android, nous voulons des fichiers VectorDrawable; pour cette raison, nous exportons les actifs au format SVG dans un dossier intermédiaire, puis nous les soumettons à un traitement ultérieur.
Fichiers PDF pour iOS
Curieusement, PDF est le (seul?) Format pris en charge par Xcode et OS / iOS pour l'importation et le rendu d'actifs vectoriels (
voici une courte explication des raisons techniques derrière ce choix d'Apple).
Comme nous pouvons exporter directement en PDF via Sketchtool, il n'y a pas besoin d'étapes supplémentaires pour cette plate-forme: nous enregistrons simplement les fichiers directement dans le dossier de destination, et c'est tout.
Fichiers React / JSX pour le Web
Dans le cas du Web, nous utilisons une bibliothèque de nœuds appelée
svgr qui convertit les fichiers SVG ordinaires en composants React. Mais nous voulons faire quelque chose d'encore plus puissant: nous voulons "peindre dynamiquement" l'icône lors de l'exécution, avec les couleurs provenant des jetons de conception. Pour cette raison, juste avant la conversion, nous remplaçons dans le SVG les valeurs de
remplissage des chemins qui avaient à l'origine un style partagé appliqué, avec la valeur de jeton correspondante associée à ce style.
Donc, s'il s'agit du fichier
badge-feature-like.svg exporté depuis 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>
le dernier élément / icône de
badge-feature-like.js ressemblera à ceci:
/* 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> ); };
Comme vous pouvez le voir, nous avons remplacé la valeur statique de la couleur de
remplissage du cercle par une valeur dynamique qui prend sa valeur dans les jetons de conception (celles-ci seront mises à la disposition du composant React
<Icon /> via l'API Context, mais c'est une autre histoire).
Ce remplacement est rendu possible grâce aux métadonnées Sketch pour l'actif stocké dans l'objet
Metadata des actifs : en bouclant récursivement à travers les couches de l'actif, il est possible de créer un sélecteur DOM (dans le cas ci-dessus, ce serait
#Icons # badge-feature- comme #circle ) et l'utiliser pour trouver le nœud dans l'arbre SVG, et remplacer la valeur de son attribut
fill (pour cette opération, nous utilisons la bibliothèque
cheerio ).
Fichiers vectoriels pour Android
Android prend en charge les graphiques vectoriels en utilisant son format vectoriel personnalisé, appelé
VectorDrawable . Habituellement, la conversion de SVG en VectorDrawable se fait
directement dans Android Studio par les développeurs. Mais ici, nous voulions automatiser l'ensemble du processus, nous devions donc trouver un moyen de les convertir via du code.
Après avoir examiné différentes bibliothèques et outils, nous avons décidé d'utiliser une bibliothèque appelée
svg2vectordrawable . Non seulement il est activement maintenu (au moins, meilleur que les autres que nous avons trouvés) mais il est également plus complet.
Le fait est que VectorDrawable n'est pas en parité de fonctionnalités avec SVG: certaines des fonctionnalités avancées de SVG (par exemple les dégradés radiaux, les masques complexes, etc.)
ne sont
pas prises en charge , et certaines n'ont
été prises en charge que récemment (avec Android API 24 et supérieur). Un inconvénient est que dans Android pré-24,
la règle de remplissage "pair-impair" n'est pas prise en charge . Mais chez Badoo, nous devons prendre en charge Android 5 et supérieur. C'est pourquoi, comme expliqué ci-dessus, pour Android, nous devons convertir chaque chemin des fichiers Sketch en un remplissage "non nul".
Potentiellement, les concepteurs pourraient le faire manuellement:

mais cela pourrait facilement être ignoré, et donc être sujet à l'erreur humaine.
Pour cette raison, nous avons ajouté une étape supplémentaire dans notre processus pour Android, où nous convertissons automatiquement tous les chemins en
non nul dans le Sketch JSON. C'est ainsi que lorsque nous exportons les icônes au format SVG, elles sont déjà dans ce format, et chaque VectorDrawable généré est également compatible avec les appareils Android 5.
Le fichier final
badge-feature-like.xml dans ce cas ressemble à ceci:
<!-- 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>
Comme vous pouvez le voir, également dans les fichiers VectorDrawable, nous injectons des noms de variables pour les couleurs de
remplissage , qui sont associées aux jetons de conception via des styles personnalisés dans les applications Android.
Voici à quoi ressemble VectorDrawable une fois importé dans Android Studio:
Un exemple d'icône VectorDrawable importée dans Android StudioUne chose à noter dans ce cas: Android Studio a une façon très stricte et normative d'organiser les actifs: pas de dossier imbriqué et tous les noms en minuscules! Cela signifiait que nous devions trouver un format légèrement différent pour leurs noms d'icônes: dans le cas d'un actif en cours d'expérimentation, son nom sera quelque chose comme
ic_icon-name__experiment-name__variant-name .
Dictionnaire JSON comme bibliothèque de ressources
Une fois que les fichiers de ressources sont enregistrés dans leur format final, la dernière chose qui reste à faire est de sauvegarder toutes les méta-informations collectées pendant le processus de construction, et de les stocker dans un "dictionnaire", afin qu'elles puissent être mises à disposition plus tard lorsque les actifs sont importés et consommés par la base de code des différentes plateformes.
Après avoir extrait la liste plate des icônes de l'objet
assetsMetadata , nous la
parcourons et pour chaque élément, nous vérifions:
- si c'est un atout normal (par exemple tabbar-livestream ), et si c'est le cas, nous le gardons simplement;
- s'il s'agit d'une variante dans un test AB (par exemple experiment / tabbar-livestream [variant] ), nous associons son nom, son chemin, le test AB et les noms de variantes, aux propriétés abtests de l'actif "de base" (dans ce cas, tabbar- livestream ), puis nous supprimons l'entrée de variante de la liste / objet (seul le "base" compte);
- s'il s'agit d'une variante "fantôme", nous supprimons le fichier, puis supprimons l'entrée de la liste / objet.
Une fois la boucle terminée, le dictionnaire contiendra la liste de toutes et uniquement les icônes "de base" (et leurs tests AB, si en cours d'expérimentation). Pour chacun d'eux, il contiendra son nom, sa taille, son chemin d'accès et, dans le cas où une icône est en cours de test AB, les informations sur les différentes options de l'actif.
Ce dictionnaire est ensuite enregistré au format JSON dans le dossier de destination de la
marque et de la
plateforme . Voici, par exemple, le fichier
assets.json généré pour l'application "Blendr" sur "web mobile":
{ "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" } } }, ... } }
La toute dernière étape consiste à compresser tous les dossiers de
ressources dans. fichiers
zip , afin qu'ils puissent être téléchargés plus facilement.
Le résultat final
Le processus décrit ci-dessus - depuis le clonage et la manipulation initiaux des fichiers Sketch, jusqu'à l'exportation (et la conversion) des actifs dans le format souhaité pour chaque plate-forme prise en charge, jusqu'au stockage des méta-informations collectées dans une bibliothèque d'actifs - est répété pour chaque marque déclarée dans le script de construction.
Vous trouverez ci-dessous une capture d'écran de la structure des dossiers
src et
dist , une fois le processus de génération terminé:
Structure des dossiers "src" et "dist" après la fin du processus de construction.À ce stade, avec une simple commande, il est possible de télécharger toutes les ressources (fichiers JSON, fichiers ZIP et fichiers d'actifs) dans un référentiel distant, et de les rendre disponibles pour toutes les différentes plates-formes, pour télécharger et consommer dans leurs bases de code.
(La façon dont les plates-formes réelles récupèrent et traitent les actifs - via des scripts personnalisés qui ont été créés ad hoc à cet effet - dépasse le cadre de cet article. Mais cela sera probablement couvert très bientôt dans d'autres articles de blog dédiés, par l'un des d'autres développeurs qui ont travaillé avec moi sur ce projet).
Conclusions (et leçons apprises en cours de route)
J'ai toujours aimé
Sketch . Pendant des années, il a été l'outil "de facto" de choix pour la conception (et le développement) de sites Web et d'applications. J'étais donc très intéressé et curieux d'explorer des intégrations possibles comme
html-sketchapp ou des outils similaires, chapeau que nous pourrions utiliser dans nos workflows et pipelines.
Ce flux (idéal) a toujours été le
Saint-Graal pour moi (
et bien d'autres ):

Le croquis en tant qu'outil de conception peut être imaginé comme une "cible" possible de la base de code.
Mais je dois admettre que j'ai récemment commencé à me demander si Sketch était toujours le bon outil, en particulier dans le contexte d'un système de conception. J'ai donc commencé à explorer de nouveaux outils comme
Figma , avec ses API ouvertes, et
Framer X , avec son incroyable intégration avec React, car je ne voyais pas d'efforts équivalents de Sketch pour progresser vers l'intégration avec du code (quel que soit le code).
Eh bien, ce projet a changé d'avis. Pas complètement, mais certainement beaucoup.
Peut-être que Sketch n'expose pas officiellement ses API, mais la façon dont ils ont construit la structure interne de leurs fichiers est certainement une sorte d'API "non officielle". Ils auraient pu utiliser des noms cryptiques ou masquer les clés des objets JSON; ils ont plutôt opté pour une convention de dénomination sémantique claire, facile à lire et lisible par l'homme. Je ne peux pas penser que c'est simplement accidentel.
Le fait que les fichiers Sketch puissent être manipulés m'a ouvert un large éventail de développements et d'améliorations futurs possibles. Des plugins pour valider la dénomination, le style et la structure des couches pour les icônes, aux intégrations possibles avec notre wiki et la documentation de notre système de conception (dans les deux sens), en passant par la création d'applications Node hébergées dans
Electron ou
Carlo pour faciliter la plupart des tâches répétitives que les concepteurs doivent entreprendre.
Un bonus inattendu de ce projet (du moins pour moi) est que maintenant les fichiers Sketch avec les "icônes Cosmos" sont devenus une "source de vérité", tout comme ce qui s'est passé avec le
système de conception Cosmos . Si une icône n'est pas là, elle n'existe pas dans la base de code (ou mieux, elle ne devrait pas exister: mais au moins nous savons que c'est une exception). Je sais que c'est plutôt évident maintenant, mais ce n'était pas le cas auparavant, du moins pour moi.
Ce qui a commencé comme un projet MVP est rapidement devenu une plongée profonde (littéralement) dans les fichiers internes de Sketch, avec la réalisation que ceux-ci peuvent être manipulés. Nous ne savons pas encore où tout cela mènera, mais jusqu'à présent, cela a été un succès. Les concepteurs, les développeurs, les PM et les parties prenantes conviennent tous que cela va économiser beaucoup de travail manuel pour tout le monde et éviter beaucoup d'erreurs potentielles. Mais cela ouvrira également la porte à des utilisations d'icônes jusqu'alors impossibles.
Une dernière chose: ce que j'ai décrit dans ce long article est un pipeline que nous avons construit ici pour résoudre
nos problèmes particuliers, et il est donc nécessairement super personnalisé pour
notre contexte. Gardez à l'esprit qu'il peut ne pas convenir à
vos besoins commerciaux ou être adapté à
votre contexte.
Mais ce qui est important pour moi, et ce que je voulais partager, c'est que cela peut être fait. Peut-être de différentes manières, avec différentes approches et différents formats de sortie, impliquant peut-être moins de complexité (c'est-à-dire: vous n'avez peut-être pas besoin du multimarquage et des tests AB). Mais maintenant, vous pouvez automatiser le flux de travail impliqué dans la livraison de vos icônes avec un script Node.js personnalisé et Sketch.
Trouvez votre propre façon de le faire. C'est amusant (et relativement facile).
Crédits
Cet énorme projet a été développé en collaboration avec
Nikhil Verma (Mobile Web), qui a créé la première version du script de construction, et
Artem Rudoi (Android) et
Igor Savelev (iOS), qui ont développé les scripts qui importent et consomment les actifs dans leur plates-formes natives respectives. Merci, les amis, ce fut un plaisir de travailler avec vous sur ce projet et de le voir prendre vie.