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
- Crie uma pasta
schematics
: Na raiz da sua biblioteca, crie uma pasta chamadaschematics
. - Crie uma pasta
ng-add
: Dentro da pastaschematics
, crie uma pasta chamadang-add
para o seu primeiro Schematic. - Crie o arquivo
collection.json
: Na raiz da pastaschematics
, crie um arquivo chamadocollection.json
. - 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
- $schema:
- Especifica o caminho para o esquema de coleção do Angular Devkit, garantindo que o arquivo
collection.json
segue a estrutura correta.
- Especifica o caminho para o esquema de coleção do Angular Devkit, garantindo que o arquivo
- schematics:
- Este objeto contém a lista de schematics que compõem a coleção.
- 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
: Quandotrue
, o schematic não aparece na lista pública de schematics disponíveis.
- Outros schematics (como dashboard, table, navigation, etc.):
- Cada um desses schematics possui uma descrição, um caminho de
factory
, umschema
, e possíveisaliases
. - Eles são usados para gerar componentes específicos do Angular Material, como dashboards, tabelas de dados, navegadores laterais, e outros componentes de interface.
- Cada um desses schematics possui uma descrição, um caminho de
- 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:
- Navegue até a pasta
ng-add
: Acesse o diretório<raiz-da-biblioteca>/schematics/ng-add
. - Crie o arquivo principal
index.ts
: Este arquivo conterá a função de fábrica do seu Schematic. - 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 tipoSchema
que contém as opções passadas pelo usuário.
- Esta função é a entrada principal do seu schematic para o comando
- 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:
- Instala a Biblioteca:
- O Angular CLI instala a versão mais recente da biblioteca usando o gerenciador de pacotes configurado.
- Executa o Schematic:
- O
ng add
executa o schematic especificado na configuração (collection.json
), chamando a funçãongAdd
.
- O
- 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, ondeTree
representa o sistema de arquivos virtual,Rule
é uma função que transforma oTree
, eSchematicContext
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, enquantoRunSchematicTask
executa outro schematic.addPackageToPackageJson
,getPackageVersionFromPackageJson
: Funções utilitárias para modificar e ler opackage.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
dopackage.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 fallback0.0.0-NG
.
- Adiciona Dependências ao
package.json
:- Verifica se
@angular/material
já está nopackage.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.
- Verifica se
- 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.
- Adiciona uma tarefa de instalação de pacotes (
- 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.
- Após a instalação dos pacotes, executa o schematic
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 aopackage.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çãodependencies
dopackage.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çãodevDependencies
dopackage.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.
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!