Gerando ícones de várias plataformas e com várias marcas com o Sketch e um script Node.js. - Parte # 2



Esta é a segunda parte de uma postagem sobre a criação de um pipeline que pode pegar um arquivo de esboço e exportar todos os ícones incluídos no arquivo, em diferentes formatos, para diferentes plataformas, com a possibilidade de a AB testar cada ícone.

Você pode ler a primeira parte do post aqui .



Os arquivos do Sketch, com todos os ícones coletados, estilizados e nomeados corretamente, estavam prontos. Agora era hora de começar a escrever o código.

Basta dizer que o processo foi uma tentativa e erro: após o importante núcleo inicial do código, desenvolvido pelo líder da minha equipe Nikhil Verma (que estabeleceu as bases do script), passei por um processo incremental que exigia pelo menos três fases de refatoração e algumas revisões. Por esse motivo, não entrarei em muitos detalhes sobre como o script foi desenvolvido, mas focarei em como o script funciona hoje, em sua forma final.

O script de construção


O script de construção - escrito em Node.js - é relativamente direto em seu fluxo: uma vez importadas as dependências, declaradas a lista de arquivos do Sketch a serem processados ​​(como uma lista de marcas e, para cada marca, uma lista de arquivos para essa marca) e verificou se o Sketch está instalado no cliente, o script faz um loop na matriz de marcas e, para cada uma delas, executa estas etapas em sequência:

  1. Obtenha os tokens de design para a marca (precisamos dos valores de cor)
  2. Clone os arquivos de esboço associados à marca, descompacte-os para expor os arquivos JSON internos e manipule alguns dos valores internos desses arquivos JSON (mais sobre isso mais adiante)
  3. Leia os meta-dados relevantes dos arquivos JSON do Sketch ( document.json , meta.json e pages / pageUniqueID.json ); em particular, precisamos da lista de estilos compartilhados e da lista de ativos / ícones contidos nos arquivos
  4. Após algumas manipulações adicionais dos arquivos Sketch JSON, remova-os e, usando os arquivos de esboço (clonados e atualizados), exporte e gere os arquivos finais de saída para as três plataformas (iOS, Android, Mobile Web)

Você pode visualizar as partes relevantes do script de construção principal aqui:

// ... 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 }); }); } 

Na verdade, todo o código do pipeline é muito mais complexo que isso, e a complexidade está nas funções prepareSketchFiles , getSketchMetadata e generateAssets [format] [platform] . Vou tentar explicá-los com mais detalhes abaixo.

Preparando os arquivos de esboço


A primeira etapa do processo de criação é a preparação dos arquivos do Sketch, para que possam ser usados ​​posteriormente para a exportação dos ativos para as diferentes plataformas.

Os arquivos associados à marca - para o Blendr, por exemplo, os arquivos icons_common.sketch e icons_blendr.sketch - são inicialmente clonados em uma pasta temporária (mais precisamente, em uma subpasta com o nome da marca que está sendo processada) e descompactados.

Em seguida, os arquivos JSON internos são processados, com um prefixo adicionado aos ativos que serão submetidos ao teste AB, para que, quando exportados, sejam salvos em uma subpasta com um nome predefinido (o nome exclusivo da experiência). Para entender quais ativos estão sendo testados, basta verificar se o nome da página em que estão armazenados no Sketch é prefixado com "XP_" .


Uma comparação dos nomes das camadas, dentro dos arquivos do Sketch, antes e depois da atualização.

No exemplo acima, quando exportados, os ativos serão salvos na subpasta "this__is_an_experiment" , com um nome de arquivo "icon-name [variant-name] .ext" .

Lendo os metadados do esboço


A segunda etapa importante do processo é obter todos os metadados relevantes dos arquivos do Sketch, em particular dos arquivos JSON internos. Como explicado acima, esses arquivos são os dois arquivos principais ( document.json e meta.json ) e os arquivos de páginas ( pages / pageUniqueId.json ).

O arquivo document.json é usado para obter a lista dos estilos compartilhados, que aparecem na propriedade do 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, armazenamos algumas informações básicas em um objeto de valor-chave. Isso será usado posteriormente sempre que precisarmos recuperar o nome de um estilo com base em seu ID exclusivo (no Sketch, a propriedade 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, }; }); 


Neste ponto, passamos para o arquivo meta.json para obter a lista de páginas, em particular precisamos do ID e nome exclusivos :

 { "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]" } } }, 

Então, para cada página, lemos o arquivo JSON correspondente na pasta pages (como já foi dito, o nome do arquivo é [pageUniqueId] .json ) e examinamos os ativos contidos nessa página (eles aparecem como camadas). Dessa forma, para cada ícone, obtemos seu nome, sua largura / altura, os metadados do Sketch para o ícone dessa camada e, se estiver em uma página de experiência, o nome do teste AB em questão e o nome da variante para esse ícone.

Observe : o objeto "page.json" é muito complexo, então não vou entrar aqui. Se você estiver curioso e quiser ver como é, sugiro que você crie um novo arquivo de esboço em branco, adicione algum conteúdo e salve; renomeie sua extensão em zip, descompacte-a e procure em um dos arquivos que aparecem na pasta "páginas".

Ao processar as pranchetas, também criamos uma lista de experimentos (com seus ativos correspondentes) que serão usados ​​posteriormente para determinar quais opções de ícone são usadas e para qual experimento, associando o nome das opções de ícone ao objeto "base do ícone".

Para cada arquivo de Sketch que está sendo processado associado à marca, produzimos um objeto assetsMetadata com a seguinte aparência:

 { "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 você pode ver, o mesmo "ícone" (neste caso , barra de navegação de edição ) pode ter vários "ativos" associados a ele, em termos de experimentos. Mas o mesmo ícone pode aparecer com o mesmo nome em um segundo arquivo de Sketch associado à marca, e isso é muito útil: é o truque que usamos, para compilar um conjunto comum de ícones e, em seguida, definir variantes de ícones diferentes específicas, dependendo da marca.

Por isso, declaramos os arquivos de esboço associados a cada marca em particular como uma matriz:

 const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'], }; 

Porque neste caso a ordem é importante. De fato, na função getSketchMetadata , chamada pelo script de construção, não retornamos os objetos assetsMetadata (um por arquivo) como uma lista, mas fazemos uma mesclagem profunda de cada objeto, um no outro, e então nós retorne um único objeto assetsMetadata mesclado.

Isso nada mais é do que a mesclagem "lógica" dos arquivos do Sketch e seus ativos em um único arquivo. Mas a lógica não é tão simples quanto parece. Aqui está o esquema que tivemos que criar para descobrir o que acontece quando há ícones com o mesmo nome (possivelmente em teste AB) em arquivos diferentes associados à mesma marca:


O esquema lógico de como a "substituição" do mesmo ícone funciona, entre um conjunto comum / compartilhado de ícones e ícones projetados especificamente para marcas brancas (também considerando o caso do teste AB)

Gerando os arquivos finais em diferentes formatos para diferentes plataformas


A última etapa do processo é a geração real dos arquivos de ícone com diferentes formatos para as diferentes plataformas (PDF para iOS, SVG / JSX para Web e VectorDrawable para Android).

Como você pode ver pelo número de parâmetros passados ​​para as funções generateAssets [formato] [plataforma], essa é a parte mais complexa do pipeline. Aqui é onde o processo começa a se dividir e divergir para as diferentes plataformas. Veja abaixo o fluxo lógico completo do script e como a parte relacionada à geração dos ativos se divide em três fluxos semelhantes, mas não idênticos:



Para gerar os ativos finais com as cores corretas associadas à marca que está sendo processada, precisamos fazer outro conjunto de manipulações nos arquivos JSON do Sketch: passamos iterativamente por todas as camadas que têm um estilo compartilhado aplicado e substituímos o valores de cores com as cores dos tokens de design da marca.

Para a geração Android, é necessária uma manipulação extra (mais sobre isso posteriormente): alteramos a propriedade de regra de preenchimento de cada camada de ímpar par para diferente de zero (isso é controlado pela propriedade "windingRule" no objeto JSON, em que " 1 "significa" ímpar par "e" 0 "significa" diferente de zero ").

Depois de concluir essas manipulações, compactamos os arquivos JSON do Sketch de volta em um arquivo de Sketch padrão, para que possam ser processados ​​para exportar os ativos com as propriedades atualizadas (os arquivos clonados e atualizados são arquivos de Sketch absolutamente normais: eles podem ser abertos no Sketch , visualizado, editado, salvo etc.).

Nesse ponto, podemos usar o sketchtool ( em um wrapper de nó ) para exportar automaticamente todos os ativos em formatos específicos para plataformas específicas. Para cada arquivo associado a uma marca (mais corretamente, sua versão clonada e atualizada), executamos este comando:

 sketchtool.run(`export slices ${cloneSketchFile} --formats=svg <i>--scales=1 </i>--output=${destinationFolder} --overwriting`); 

Como você pode imaginar, esse comando exporta os ativos em um formato específico, aplicando uma escala opcional (por enquanto, sempre mantemos a escala original), para uma pasta de destino. A opção --overwriting é a chave aqui: da mesma maneira que fazemos uma "mesclagem profunda" dos objetos assetsMetadata (que equivale a uma "mesclagem lógica" dos arquivos do Sketch), quando exportamos, fazemos isso de vários arquivos para a mesma pasta (exclusiva por marca / plataforma). Isso significa que, se um ativo - identificado pelo nome da camada - já existia em um arquivo de esboço anterior, ele será substituído pela exportação a seguir. O que, novamente, nada mais é do que uma operação de "mesclagem".

Nesse caso, porém, podemos ter alguns ativos que são "fantasmas". Isso acontece quando um ícone é testado AB em um arquivo, mas substituído em um arquivo subsequente. Nesses casos, os arquivos variantes são exportados para a pasta de destino, referenciada no objeto assetsMetadata como ativo (com sua chave e propriedades), mas não associada a nenhum ativo "base" (devido à mesclagem profunda dos objetos assetsMetadata ). Esses arquivos serão removidos em uma etapa posterior, antes da conclusão do processo.



Como mencionado acima, precisamos de diferentes formatos finais para diferentes plataformas. Para iOS, queremos arquivos PDF e podemos exportá-los diretamente com o comando sketchtool . Enquanto, para a Web móvel, queremos arquivos JSX, e para Android, arquivos VectorDrawable; por esse motivo, exportamos os ativos no formato SVG para uma pasta intermediária e os sujeitamos a processamento adicional.

Arquivos PDF para iOS


Estranhamente, o PDF é o formato (apenas?) Suportado pelo Xcode e OS / iOS para importar e renderizar ativos vetoriais ( aqui está uma breve explicação dos motivos técnicos por trás dessa escolha da Apple).

Como podemos exportar diretamente em PDF via Sketchtool, não há necessidade de etapas extras para esta plataforma: simplesmente salvamos os arquivos diretamente na pasta de destino, e é isso.

Arquivos React / JSX para web


No caso da Web, usamos uma biblioteca Node chamada svgr, que converte arquivos SVG simples em componentes React. Mas queremos fazer algo ainda mais poderoso: queremos "pintar dinamicamente" o ícone em tempo de execução, com as cores provenientes dos tokens de design. Por esse motivo, imediatamente antes da conversão, substituímos no SVG os valores de preenchimento dos caminhos que originalmente tinham um estilo compartilhado aplicado, pelo valor do token correspondente associado a esse estilo.

Portanto, se este é o arquivo badge-feature-like.svg exportado do 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> 

o recurso / ícone badge-feature-like.js final terá a seguinte aparência:

 /* 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 você pode ver, substituímos o valor estático da cor de preenchimento do círculo por um dinâmico, que recebe o valor dos tokens de design (eles serão disponibilizados para o componente React <Icon /> via API de contexto, mas isso é outra história).

Essa substituição é possível através dos metadados do Sketch para o ativo armazenado no objeto de metadados do ativo : fazendo um loop recursivo pelas camadas do ativo, é possível criar um seletor DOM (no caso acima, seria #Icons # badge-feature- como #circle ) e use-o para encontrar o nó na árvore SVG e substitua o valor do atributo de preenchimento (para esta operação, usamos a biblioteca cheerio ).

Arquivos desenháveis ​​para Android


O Android suporta gráficos vetoriais usando seu formato vetorial personalizado, chamado VectorDrawable . Normalmente, a conversão de SVG para VectorDrawable é feita diretamente no Android Studio pelos desenvolvedores. Mas aqui queríamos automatizar todo o processo, portanto, precisamos encontrar uma maneira de convertê-los via código.

Depois de examinar diferentes bibliotecas e ferramentas, decidimos usar uma biblioteca chamada svg2vectordrawable . Não apenas é mantido ativamente (pelo menos, melhor que os outros que encontramos), mas também é mais completo.

O fato é que o VectorDrawable não tem paridade de recursos com o SVG: alguns dos recursos avançados do SVG (por exemplo, gradientes radiais, máscaras complexas etc.) não são suportados e alguns deles ganharam suporte apenas recentemente (com a API Android 24 e superior). Uma desvantagem disso é que, no Android anterior a 24, a regra de preenchimento "ímpares pares" não é suportada . Mas no Badoo, precisamos oferecer suporte ao Android 5 e superior. É por isso que, como explicado acima, para Android, precisamos converter todos os caminhos nos arquivos do Sketch para preenchimento "diferente de zero".

Potencialmente, os designers podem fazer isso manualmente:



mas isso pode ser facilmente esquecido e, portanto, propenso a erros humanos.

Por esse motivo, adicionamos uma etapa extra em nosso processo para Android, onde convertemos automaticamente todos os caminhos para diferentes de zero no JSON do Sketch. Isso ocorre quando exportamos os ícones para SVG, eles já estão nesse formato e cada VectorDrawable gerado também é compatível com os dispositivos Android 5.

O arquivo badge-feature-like.xml final, neste caso, é assim:

 <!-- 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 você pode ver, também nos arquivos VectorDrawable, injetamos nomes de variáveis ​​para as cores de preenchimento , que são associadas aos tokens de design por meio de estilos personalizados nos aplicativos Android.

É assim que o VectorDrawable é importado no Android Studio:


Um exemplo de ícone VectorDrawable importado para o Android Studio

Uma coisa a observar neste caso: o Android Studio tem uma maneira muito estrita e prescritiva de organizar os ativos: nenhuma pasta aninhada e todos os nomes em minúsculas! Isso significava que tivemos que criar um formato ligeiramente diferente para os nomes de seus ícones: no caso de um ativo em experiência, seu nome será algo como ic_icon-name__experiment-name__variant-name .

Dicionário JSON como biblioteca de ativos


Depois que os arquivos de ativos são salvos em seu formato final, a última coisa a ser feita é salvar todas as metainformações coletadas durante o processo de compilação e armazená-las em um "dicionário", para que possam ser disponibilizadas posteriormente quando os ativos são importados e consumidos pela base de código das diferentes plataformas.

Depois de extrair a lista simples de ícones do objeto assetsMetadata , fazemos um loop sobre ele e para cada item, verificamos:

  • se é um ativo normal (por exemplo, tabbar-livestream ) e, se for, apenas o mantemos;
  • se for uma variante em um teste AB (por exemplo, experiment / tabbar-livestream [variant] ), associaremos seu nome, caminho, teste AB e nomes de variantes às abstests de propriedades do ativo "base" (neste caso, tabbar- livestream ) e, em seguida, removemos a entrada variante da lista / objeto (apenas a "base" conta);
  • se for uma variante "fantasma", excluiremos o arquivo e removeremos a entrada da lista / objeto.

Depois que o loop for concluído, o dicionário conterá a lista de todos e somente os ícones "base" (e seus testes AB, se estiverem sendo experimentados). Para cada um deles, ele conterá seu nome, tamanho, caminho e, no caso de um ícone estar em teste AB, as informações sobre as diferentes opções do ativo.

Este dicionário é salvo no formato JSON na pasta de destino da marca e plataforma . Aqui, por exemplo, está o arquivo assets.json gerado para o aplicativo "Blendr" na "web móvel":

 { "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" } } }, ... } } 

A última etapa é compactar todas as pastas de ativos . arquivos zip , para que possam ser baixados mais facilmente.

O resultado final


O processo descrito acima - da clonagem e manipulação inicial dos arquivos do Sketch, à exportação (e conversão) dos ativos no formato desejado para todas as plataformas suportadas e ao armazenamento das metainformações coletadas em uma biblioteca de ativos - é repetido para cada marca declarada no script de construção.

Abaixo está uma captura de tela da aparência da estrutura das pastas src e dist , assim que o processo de compilação for concluído:


Estrutura das pastas "src" e "dist" após a conclusão do processo de compilação.

Neste ponto, com um comando simples, é possível fazer o upload de todos os recursos (arquivos JSON, arquivos ZIP e arquivos de ativos) para um repositório remoto e disponibilizá-los para todas as plataformas, para baixar e consumir em suas bases de código.

(Como as plataformas reais recuperam e processam os ativos - por meio de scripts personalizados criados ad-hoc para esse fim - está além do escopo deste artigo. Mas isso provavelmente será abordado muito em breve em outras postagens de blog dedicadas, por um dos outros desenvolvedores que trabalharam comigo neste projeto).

Conclusões (e lições aprendidas ao longo do caminho)


Eu sempre amei o Sketch . Durante anos, tem sido a ferramenta "de fato" escolhida para o design (e desenvolvimento) de aplicativos e web. Por isso, fiquei muito interessado e curioso em explorar possíveis integrações, como html-sketchapp ou ferramentas similares, o que poderíamos usar em nossos fluxos de trabalho e pipelines.

Esse fluxo (ideal) sempre foi o santo graal para mim ( e muitos outros ):



O esboço como uma ferramenta de design pode ser imaginado como um possível "destino" da base de código.

Mas tenho que admitir que recentemente comecei a me perguntar se o Sketch ainda era a ferramenta certa, especialmente no contexto de um sistema de design. Então, comecei a explorar novas ferramentas como o Figma , com suas APIs abertas, e o Framer X , com sua incrível integração com o React, porque não estava vendo esforços equivalentes do Sketch para avançar para a integração com o código (qualquer que seja o código).

Bem, este projeto mudou de idéia. Não completamente, mas definitivamente muito.

Talvez o Sketch não exponha oficialmente suas APIs, mas certamente a maneira pela qual eles construíram a estrutura interna de seus arquivos é uma espécie de API "não oficial". Eles poderiam ter usado nomes enigmáticos ou ofuscar as chaves nos objetos JSON; em vez disso, optaram por uma convenção de nomenclatura semântica clara, fácil de ler, legível por humanos. Não acho que isso seja apenas acidental.

O fato de os arquivos do Sketch poderem ser manipulados abriu minha mente para uma ampla gama de possíveis desenvolvimentos e melhorias futuros. Desde plugins para validar a nomeação, estilo e estrutura das camadas para os ícones, até possíveis integrações com nosso wiki e nossa documentação do sistema de design (em ambas as direções), através da criação de aplicativos Node hospedados em Electron ou Carlo para facilitar muitos dos tarefas repetitivas que os designers precisam realizar.

Um bônus inesperado deste projeto (pelo menos para mim) é que agora os arquivos do Sketch com os "ícones do Cosmos" se tornaram uma "fonte de verdade", semelhante ao que aconteceu com o sistema de design do Cosmos . Se um ícone não estiver lá, ele não existe na base de código (ou melhor, não deveria existir: mas pelo menos sabemos que é uma exceção). Eu sei que é meio óbvio agora, mas não era antes, pelo menos para mim.

O que começou como um projeto MVP, logo se tornou um mergulho profundo (literalmente) nas partes internas dos arquivos do Sketch, com a constatação de que eles podem ser manipulados. Ainda não sabemos aonde tudo isso levará, mas até agora tem sido um sucesso. Designers, desenvolvedores, PMs e partes interessadas concordam que isso economizará muito trabalho manual para todos e evitará muitos erros em potencial. Mas também abrirá as portas para os usos dos ícones que eram impossíveis até agora.

Uma última coisa: o que descrevi neste longo post é um pipeline que criamos aqui para resolver nossos problemas específicos e, portanto, é necessariamente super-personalizado para o nosso contexto. Lembre-se de que pode não atender às necessidades da sua empresa ou ser apropriado ao seu contexto.

Mas o que é importante para mim e o que eu queria compartilhar é que isso pode ser feito. Talvez de maneiras diferentes, com abordagens diferentes e formatos de saída diferentes, talvez envolvendo menos complexidade (ou seja, talvez você não precise da marca múltipla e do teste AB). Mas agora você pode automatizar o fluxo de trabalho envolvido na entrega de seus ícones com um script Node.js. personalizado e o Sketch.

Encontre sua própria maneira de fazê-lo. É divertido (e relativamente fácil).

Créditos


Esse grande projeto foi desenvolvido em colaboração com Nikhil Verma (Web móvel), que criou a primeira versão do script de compilação, e Artem Rudoi (Android) e Igor Savelev (iOS), que desenvolveram os scripts que importam e consomem os ativos em seus sites . respectivas plataformas nativas. Obrigado pessoal, foi um prazer trabalhar com você nesse projeto e vê-lo ganhar vida.

Source: https://habr.com/ru/post/pt441042/


All Articles