Experts in Angular

Angular CLIParte 1: Schematics para Bibliotecas no Angular
Schematics para Bibliotecas: Integrando sua Criação ao Ecossistema Angular

Parte 1: Schematics para Bibliotecas no Angular

Desenvolvedores de bibliotecas Angular têm a oportunidade de aprimorar a experiência de seus usuários, fornecendo Schematics que se integram perfeitamente ao Angular CLI.
Com esses Schematics, os usuários podem instalar a biblioteca em seus projetos com o comando ng add, gerar artefatos específicos da biblioteca com ng generate e até mesmo atualizar seus projetos para novas versões da biblioteca que introduzem mudanças significativas usando o ng update.

Esses três tipos de Schematics podem ser agrupados em uma coleção que você empacota junto com sua biblioteca, oferecendo um conjunto completo de ferramentas para seus usuários interagirem com sua criação de forma intuitiva e eficiente.

Imagine que você criou uma biblioteca de componentes para construir interfaces de naves espaciais futuristas. Com Schematics, você pode automatizar a instalação da biblioteca, a geração de componentes específicos e a atualização do projeto para novas versões, tornando sua biblioteca mais acessível e atraente para outros desenvolvedores.

Criando uma Coleção de Schematics: O Primeiro Passo para a Integração

Para iniciar a integração da sua biblioteca com o Angular CLI, você precisa criar os arquivos necessários para a coleção de Schematics. Vamos seguir um passo a passo para adicionar o suporte inicial sem modificar nenhum arquivo do projeto.

Criando a Estrutura da Coleção

  1. Crie uma pasta schematics: Na raiz da sua biblioteca, crie uma pasta chamada schematics.
  2. Crie uma pasta ng-add: Dentro da pasta schematics, crie uma pasta chamada ng-add para o seu primeiro Schematic.
  3. Crie o arquivo collection.json: Na raiz da pasta schematics, crie um arquivo chamado collection.json.
  4. Edite o arquivo collection.json: Defina o esquema inicial para sua coleção.
// projects/my-lib/schematics/collection.json 
{
  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "ng-add": {
      "description": "Adiciona minha biblioteca ao projeto.",
      "factory": "./ng-add/index#ngAdd",
      "schema": "./ng-add/schema.json"
    }
  }
}

O caminho $schema é relativo ao esquema de coleção do Angular Devkit.
O objeto schematics descreve os Schematics nomeados que fazem parte desta coleção.
A primeira entrada é para um Schematic chamado ng-add. Ele contém a descrição e aponta para a função de fábrica que é chamada quando seu Schematic é executado.

Biblioteca do Angular Material como exemplo

O arquivo <a target="_blank" href="https://github.com/angular/components/tree/main/src/material/schematics" rel="noopener">collection.json</a> do Angular Material é um exemplo prático de como organizar e descrever os Schematics de uma biblioteca. Ele fornece informações claras sobre cada Schematic, incluindo sua descrição, função de fábrica, esquema de opções e aliases, facilitando a integração da biblioteca com o Angular CLI e a utilização de seus recursos pelos desenvolvedores.

Análise do collection.json do Angular Material

O arquivo collection.json no repositório do Angular Material descreve as várias operações que podem ser realizadas usando os schematics.
Aqui está uma visão geral dos elementos mais importantes:

{
  "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "ng-add": {
      "description": "Adds Angular Material to the application without affecting any templates",
      "factory": "./ng-add/index",
      "schema": "./ng-add/schema.json",
      "aliases": ["material-shell", "install"],
      "hidden": true
    },
    "ng-add-setup-project": {
      "description": "Sets up the specified project after the ng-add dependencies have been installed.",
      "private": true,
      "factory": "./ng-add/setup-project",
      "schema": "./ng-add/schema.json",
      "hidden": true
    },
    "dashboard": {
      "description": "Create a card-based dashboard component",
      "factory": "./ng-generate/dashboard/index",
      "schema": "./ng-generate/dashboard/schema.json",
      "aliases": ["material-dashboard"]
    },
    "table": {
      "description": "Create a component that displays data with a data-table",
      "factory": "./ng-generate/table/index",
      "schema": "./ng-generate/table/schema.json",
      "aliases": ["material-table"]
    },
    "navigation": {
      "description": "Create a component with a responsive sidenav for navigation",
      "factory": "./ng-generate/navigation/index",
      "schema": "./ng-generate/navigation/schema.json",
      "aliases": ["material-nav", "materialNav", "nav"]
    },
    "tree": {
      "description": "Create a file tree component.",
      "factory": "./ng-generate/tree/index",
      "schema": "./ng-generate/tree/schema.json",
      "aliases": ["material-tree"]
    },
    "addressForm": {
      "description": "Create a component with an address form",
      "factory": "./ng-generate/address-form/index",
      "schema": "./ng-generate/address-form/schema.json",
      "aliases": ["address-form", "material-address-form", "material-addressForm"]
    },
    "m3Theme": {
      "description": "Generate M3 theme",
      "factory": "./ng-generate/m3-theme/index_bundled",
      "schema": "./ng-generate/m3-theme/schema.json",
      "aliases": ["m3-theme", "M3-theme"]
    }
  }
}

Estrutura e Campos Importantes

  1. $schema:
    • Especifica o caminho para o esquema de coleção do Angular Devkit, garantindo que o arquivo collection.json segue a estrutura correta.
  2. schematics:
    • Este objeto contém a lista de schematics que compõem a coleção.
  3. ng-add:
    • Descrição: Adiciona o Angular Material ao projeto sem afetar os templates existentes.
    • factory: O caminho para o arquivo que contém a implementação do schematic.
    • schema: O caminho para o arquivo JSON que define as opções disponíveis para o schematic.
    • aliases: Nomes alternativos que podem ser usados para referenciar o schematic.
    • hidden: Quando true, o schematic não aparece na lista pública de schematics disponíveis.
  4. Outros schematics (como dashboard, table, navigation, etc.):
    • Cada um desses schematics possui uma descrição, um caminho de factory, um schema, e possíveis aliases.
    • Eles são usados para gerar componentes específicos do Angular Material, como dashboards, tabelas de dados, navegadores laterais, e outros componentes de interface.
  5. Aliases:
    • Permitem que o usuário invoque o schematic usando nomes alternativos, oferecendo flexibilidade no uso dos comandos.

Referenciando a Coleção no package.json

No arquivo package.json do seu projeto de biblioteca, adicione uma entrada "schematics" com o caminho para o seu arquivo de esquema. O Angular CLI usa essa entrada para encontrar Schematics nomeados em sua coleção ao executar comandos.

// projects/my-lib/package.json
{
  "name": "my-lib",
  "version": "0.0.1",
  ...
  "schematics": "./schematics/collection.json",
  ...
}

O Esquema Inicial

O esquema inicial que você criou informa ao CLI onde encontrar o Schematic que suporta o comando ng add. Agora você está pronto para criar esse Schematic.

Fornecendo Suporte à Instalação: Abrindo as Portas da sua Biblioteca

O Schematic ng-add é a chave de ouro para a integração da sua biblioteca ao ecossistema Angular. Ele permite que os usuários instalem sua biblioteca de forma rápida e fácil, com um simples comando no Angular CLI. Mas, além da instalação, o ng-add também pode realizar configurações iniciais e guiar o usuário pelos primeiros passos de uso da sua biblioteca.

Criando o Schematic ng-add: O Portal de Entrada

Para criar o Schematic ng-add, siga estes passos:

  1. Navegue até a pasta ng-add: Acesse o diretório <raiz-da-biblioteca>/schematics/ng-add.
  2. Crie o arquivo principal index.ts: Este arquivo conterá a função de fábrica do seu Schematic.
  3. Implemente a função de fábrica: Adicione o código-fonte da sua função de fábrica ao arquivo index.ts.
// Importa as dependências necessárias do Angular Devkit e utilitários.
import { Rule } from '@angular-devkit/schematics';
import { addRootImport } from '@schematics/angular/utility';
import { Schema } from './schema';

// Define a função de fábrica do schematic ngAdd.
export function ngAdd(options: Schema): Rule {
  // Adiciona uma importação `MyLibModule` do módulo `my-lib` na raiz do projeto do usuário.
  return addRootImport(
    options.project,
    ({ code, external }) => code`${external('MyLibModule', 'my-lib')}`,
  );
}

Explicação do Código

  • Imports Necessários:
    • Rule: Interface do Angular Schematics para definir uma regra de transformação.
    • addRootImport: Função utilitária do Angular que auxilia na adição de importações na raiz de um módulo Angular.
    • Schema: Interface que define o esquema de entrada do schematic.
  • Função ngAdd:
    • Esta função é a entrada principal do seu schematic para o comando ng add.
    • Aceita um parâmetro options do tipo Schema que contém as opções passadas pelo usuário.
  • Adição de Importação:
    • addRootImport: Utiliza-se dessa função para adicionar programaticamente uma importação no módulo raiz do projeto.
    • O método addRootImport aceita dois argumentos:
      • options.project: Nome do projeto onde a importação deve ser feita.
      • Função de Retorno: Esta função define como a importação deve ser estruturada.
        • code: Marca a string como um bloco de código.
        • external: Marca símbolos externos que precisam de importação, garantindo que as declarações de importação apropriadas sejam geradas automaticamente.

Como Funciona

Quando um usuário executa ng add para sua biblioteca, o Angular CLI:

  1. Instala a Biblioteca:
    • O Angular CLI instala a versão mais recente da biblioteca usando o gerenciador de pacotes configurado.
  2. Executa o Schematic:
    • O ng add executa o schematic especificado na configuração (collection.json), chamando a função ngAdd.
  3. Modifica o Projeto:
    • O schematic pode realizar várias tarefas, como adicionar módulos, atualizar arquivos de configuração ou mesmo criar novos arquivos.

Analisando o ng-add/index.ts do Angular Material: Desvendando os Bastidores da Instalação

Para aprofundar nosso conhecimento sobre a criação de Schematics, vamos examinar o código real do ng-add/index.ts do Angular Material. Esse arquivo é responsável por orquestrar a instalação da biblioteca no projeto do usuário, garantindo que todas as dependências sejam adicionadas e as configurações iniciais sejam aplicadas.

/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

// Importações necessárias do Angular Devkit Schematics
import {Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
import {NodePackageInstallTask, RunSchematicTask} from '@angular-devkit/schematics/tasks';
import {addPackageToPackageJson, getPackageVersionFromPackageJson} from './package-config';
import {Schema} from './schema';

/**
 * Intervalo de versão que será usado para o CDK e Angular Material se este
 * schematic tiver sido executado fora do comando `ng add` do CLI. Nessas situações,
 * pode não haver dependência de `@angular/material` no arquivo `package.json`,
 * e precisamos inserir manualmente a dependência com base no placeholder de versão de build.
 */
const fallbackMaterialVersionRange = `~0.0.0-PLACEHOLDER`;

/**
 * Ponto de entrada da fábrica do schematic para o `ng-add`. O schematic `ng-add` será
 * executado automaticamente se os desenvolvedores executarem `ng add @angular/material`.
 *
 * Como os schematics do Angular Material dependem das funções utilitárias do CDK,
 * precisamos instalar o CDK antes de carregar os arquivos schematics que importam do CDK.
 */
export default function (options: Schema): Rule {
  return (host: Tree, context: SchematicContext) => {
    // Tag de versão da dependência `@angular/core` carregada do `package.json`
    // do projeto CLI. Esta tag deve ser preferida porque todas as dependências do Angular
    // devem ter a mesma tag de versão, se possível.
    const ngCoreVersionTag = getPackageVersionFromPackageJson(host, '@angular/core');
    const materialVersionRange = getPackageVersionFromPackageJson(host, '@angular/material');
    const angularDependencyVersion = ngCoreVersionTag || `0.0.0-NG`;

    // O CLI insere `@angular/material` no `package.json` antes deste schematic ser executado.
    // Isso significa que não precisamos inserir o Angular Material novamente no `package.json`.
    // Em alguns casos, pode acontecer que este schematic seja executado fora do comando `ng add`
    // do CLI, ou Material esteja listado apenas como uma dependência de desenvolvimento.
    // Se for o caso, inserimos uma versão baseada no placeholder de versão de build atual.
    if (materialVersionRange === null) {
      addPackageToPackageJson(host, '@angular/material', fallbackMaterialVersionRange);
    }

    addPackageToPackageJson(
      host,
      '@angular/cdk',
      materialVersionRange || fallbackMaterialVersionRange,
    );
    addPackageToPackageJson(host, '@angular/forms', angularDependencyVersion);
    addPackageToPackageJson(host, '@angular/animations', angularDependencyVersion);

    // Como os schematics do Angular Material dependem das funções utilitárias do CDK,
    // precisamos instalar o CDK antes de carregar os arquivos schematics que importam do CDK.
    const installTaskId = context.addTask(new NodePackageInstallTask());

    context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]);
  };
}

Explicação Detalhada

1. Importações:
  • Rule, SchematicContext, Tree: Componentes principais do Angular Devkit para criação de schematics, onde Tree representa o sistema de arquivos virtual, Rule é uma função que transforma o Tree, e SchematicContext fornece utilitários e contexto para o schematic.
  • NodePackageInstallTask, RunSchematicTask: Tarefas que podem ser adicionadas à fila de execução de tarefas do schematic. NodePackageInstallTask instala pacotes npm, enquanto RunSchematicTask executa outro schematic.
  • addPackageToPackageJson, getPackageVersionFromPackageJson: Funções utilitárias para modificar e ler o package.json do projeto, respectivamente.
  • Schema: Interface que define as opções que podem ser passadas para o schematic.
2. Constante fallbackMaterialVersionRange:

Define um intervalo de versão padrão (~0.0.0-PLACEHOLDER) para o Material e CDK caso a versão não possa ser determinada automaticamente do package.json.

3. Função ngAdd:

Esta é a função principal executada quando ng add @angular/material é chamado.

  • Obtém Versões do Pacote:
    • ngCoreVersionTag: Tenta obter a versão do @angular/core do package.json.
    • materialVersionRange: Tenta obter a versão do @angular/material.
    • Se a versão do @angular/core não for encontrada, usa uma versão de fallback 0.0.0-NG.
  • Adiciona Dependências ao package.json:
    • Verifica se @angular/material já está no package.json. Se não, adiciona com a versão de fallback.
    • Adiciona o @angular/cdk, @angular/forms e @angular/animations, garantindo que todas as versões estejam alinhadas com o Angular Core ou usem o placeholder de versão.
  • Gerencia Instalação de Pacotes:
    • Adiciona uma tarefa de instalação de pacotes (NodePackageInstallTask) para garantir que as dependências sejam instaladas após as modificações.
  • Executa Configurações de Projeto:
    • Após a instalação dos pacotes, executa o schematic ng-add-setup-project para configurar o projeto com as novas dependências.

Em Resumo

O ng-add/index.ts do Angular Material demonstra como um Schematic pode ser utilizado para:

  • Gerenciar dependências: Verificar e adicionar dependências ao projeto do usuário.
  • Orquestrar tarefas: Agendar a execução de outras tarefas, como a instalação de pacotes npm e a execução de outros Schematics.
  • Configurar o projeto: Preparar o projeto para utilizar a biblioteca, adicionando importações, configurações e outros recursos.

Ao analisar esse exemplo, você pode se inspirar para criar seus próprios Schematics ng-add e oferecer uma experiência de instalação completa e intuitiva para os usuários da sua biblioteca Angular.

Definindo o Tipo de Dependência: Escolhendo o Local de Acomodação da sua Biblioteca

Ao criar um schematic para o comando ng add, é possível definir como as dependências da sua biblioteca serão adicionadas ao arquivo package.json do projeto que está sendo configurado. Isso é feito através da propriedade ng-add no package.json da sua biblioteca.

Configuração do Tipo de Dependência

A configuração do tipo de dependência especifica onde a dependência será adicionada dentro do package.json. Isso é particularmente útil para diferenciar entre bibliotecas que são necessárias para a execução do aplicativo em produção e aquelas que são utilizadas apenas durante o desenvolvimento ou construção do projeto.

Aqui está como você pode definir o tipo de dependência no package.json da sua biblioteca:

Exemplo de Configuração

No arquivo package.json da sua biblioteca, você pode adicionar uma entrada ng-add para configurar o comportamento do ng add:

{
  "name": "my-lib",
  "version": "0.0.1",
  ...
  "ng-add": {
    "save": "devDependencies"
  }
}
Possíveis Valores e Seus Detalhes

A propriedade save dentro de ng-add pode ter os seguintes valores:

  • false: Não adiciona o pacote ao package.json do projeto. Esta opção é útil quando a biblioteca não precisa ser listada como uma dependência porque é usada de forma transitória ou temporária.
  • true ou "dependencies": Adiciona o pacote à seção dependencies do package.json. Esta é a configuração padrão para bibliotecas que são necessárias para a execução do aplicativo em produção.
  • "devDependencies": Adiciona o pacote à seção devDependencies do package.json. Esta opção é apropriada para ferramentas de desenvolvimento, bibliotecas de teste, ou qualquer coisa que não seja necessária em produção.

Dependências de Produção vs. Desenvolvimento: Escolhendo o Destino Correto

  • Dependências de Produção (dependencies): São bibliotecas essenciais para o funcionamento da sua aplicação em produção. Elas são incluídas no pacote final e enviadas para o navegador do usuário.
  • Dependências de Desenvolvimento (devDependencies): São bibliotecas utilizadas apenas durante o desenvolvimento, como ferramentas de teste, linters e o próprio Angular CLI. Elas não são incluídas no pacote final e não são necessárias para a execução da aplicação em produção.

Escolhendo o Tipo de Dependência:

  • Biblioteca de Componentes ou Utilitários: Se sua biblioteca fornece componentes, diretivas, pipes ou outros elementos que serão utilizados diretamente na interface do usuário ou na lógica da sua aplicação, ela deve ser adicionada como uma dependência de produção.
  • Ferramentas de Desenvolvimento: Se sua biblioteca fornece ferramentas para auxiliar no desenvolvimento, como Schematics, linters ou plugins do Angular CLI, ela deve ser adicionada como uma dependência de desenvolvimento.
  • Bibliotecas Opcionais: Se sua biblioteca oferece funcionalidades opcionais que podem não ser utilizadas em todos os projetos, você pode considerar adicioná-la como uma dependência de desenvolvimento e deixar que o usuário decida se deseja incluí-la em produção.

Ao definir o tipo de dependência correto para sua biblioteca, você garante que o processo de build e implantação da sua aplicação seja otimizado, incluindo apenas as dependências essenciais para o funcionamento em produção.

Schematics para Bibliotecas: Integrando sua Criação ao Ecossistema Angular
Schematics para Bibliotecas: Integrando sua Criação ao Ecossistema Angular

Conclusão: Schematics para Bibliotecas – Abrindo as Portas para a Comunidade Angular

Nesta primeira parte da nossa jornada pelos Schematics para bibliotecas, exploramos os três tipos principais de Schematics que você pode criar para integrar sua biblioteca ao Angular CLI:

  • Schematics ng-add: Facilitam a instalação da sua biblioteca, adicionando dependências, configurações e código de inicialização ao projeto do usuário.
  • Schematics de Geração: Automatizam a criação de artefatos específicos da sua biblioteca, como componentes, diretivas e serviços, através do comando ng generate.
  • Schematics de Atualização: Auxiliam os usuários a atualizar seus projetos para novas versões da sua biblioteca, lidando com possíveis mudanças e garantindo uma migração suave.

Ao fornecer esses Schematics, você está abrindo as portas da sua biblioteca para a comunidade Angular, tornando-a mais acessível, fácil de usar e pronta para ser integrada em diversos projetos.

No próximo artigo, exploraremos em detalhes como construir seus próprios Schematics, desde a definição do esquema JSON até a implementação da lógica de transformação.
Prepare-se para desvendar os segredos da criação de Schematics e levar sua biblioteca Angular para o próximo nível!