Hogyan készíts saját Angular schematics-et a kódgeneráláshoz?

Üdvözlet, Angular fejlesztők! Ha valaha is úgy érezted, hogy túl sok időt töltesz ismétlődő kódok írásával, komponensek, szolgáltatások vagy modulok boilerplate-jének felállításával, akkor jó helyen jársz. Az Angular keretrendszer egyik legkevésbé kihasznált, mégis hihetetlenül erőteljes eszköze az Angular Schematics, amely forradalmasíthatja a fejlesztési munkafolyamatodat. Készülj fel, mert ma mélyre merülünk abban, hogyan készíthetsz saját Schematics-et a kódgenerálás automatizálásához, és hogyan növelheted jelentősen a fejlesztői hatékonyságodat.

Mi az az Angular Schematics és Miért Fontos?

Az Angular Schematics egy kódgeneráló és kódmódosító motor, amely lehetővé teszi, hogy sablonok és szabályok alapján automatizáljuk a gyakori feladatokat a projektünkben. Gondolj rá úgy, mint egy varázslatos robotra, amely elvégzi helyetted a repetitív kódolást, miközben fenntartja a projekt egységességét. Valójában az Angular CLI parancsok (mint az ng generate component vagy ng new) is Schematics-eket használnak a háttérben.

De miért is van erre szükséged? Íme néhány nyomós ok:

  • Konzisztencia: Biztosítja, hogy az összes újonnan generált fájl és kód kövesse a vállalatod vagy csapatod által meghatározott legjobb gyakorlatokat és stílusirányelveket. Nincs többé vita a mappastruktúráról vagy a fájlnevekről.
  • Sebesség és Hatékonyság: Jelentősen felgyorsítja a fejlesztést azáltal, hogy a boilerplate kód pillanatok alatt létrejön. Kevesebb gépelés, több fókusz a valódi üzleti logikára.
  • Hibacsökkentés: Az automatizált folyamatok minimalizálják az emberi hibák esélyét, amelyek a manuális kódmásolás során könnyen előfordulhatnak.
  • Refaktorálás és Migráció: Nem csak generálásra alkalmas! Schematics-ekkel automatikusan módosíthatod vagy refaktorálhatod a meglévő kódot, ami rendkívül hasznos lehet egy nagyobb átalakítás vagy API változás esetén.

Előkészületek: Mire lesz szükséged?

Mielőtt belevágnánk a Schematics írásába, győződj meg róla, hogy a következő eszközök telepítve vannak a gépeden:

  • Node.js és npm: Ezek elengedhetetlenek a JavaScript/TypeScript fejlesztéshez és a csomagkezeléshez.
  • Angular CLI: Noha a Schematics önállóan is használható, az Angular CLI kényelmes felületet biztosít a futtatásukhoz és az Angular projektkörnyezet kezeléséhez. Telepítsd a npm install -g @angular/cli paranccsal.
  • Nx (Opcionális, de Ajánlott): Az Nx egy kiterjesztett monorepo eszköz, amely nagyszerűen kezeli a Schematics projekteket, és számos beépített funkciót kínál. Ha egy nagyobb projekten dolgozol, érdemes megfontolni.

A Schematics Munkaterület Beállítása

Két fő módja van egy Schematics projekt létrehozásának:

1. Schematics Munkaterület Létrehozása az Nx Segítségével (Ajánlott)

Ha már használsz Nx-et, vagy szeretnél egy monorepo struktúrában dolgozni, ez az út a legegyszerűbb:

npx create-nx-workspace my-schematics-workspace --preset=angular --appName=my-angular-app
cd my-schematics-workspace
nx generate @nx/angular:library --name=my-schematics --publishable --importPath=@my-org/my-schematics

Ez létrehoz egy új Angular munkaterületet, majd egy publikálható könyvtárat, ami ideális a Schematics tárolására. Az Nx automatikusan konfigurálja a buildelést és a tesztelést.

2. Standalone Schematics Munkaterület Létrehozása

Ha csak egy Schematics-et szeretnél létrehozni egy már meglévő Angular projekthez, vagy önállóan kezelni, akkor a schematics-cli a barátod:

npm install -g @angular-devkit/schematics-cli
schematics blank my-custom-schematics
cd my-custom-schematics
npm install

Ez egy üres Schematics projektet hoz létre, amely tartalmazza a szükséges alapfájlokat.

A Schematics Struktúrája: Ahol a Varázslat Kezdődik

Nézzük meg, milyen kulcsfontosságú fájlok alkotják egy Schematics projektet:

collection.json: A Belépési Pont

Ez a fájl az összes Schematics listáját tartalmazza, amit a projekt kínál. Itt definiálod a Schematics nevét, leírását, és hogy melyik TypeScript fájl implementálja azt.

{
  "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "my-component-generator": {
      "description": "Generál egy új komponenst a mi stílusunkban.",
      "factory": "./src/my-component-generator/index#myComponentGenerator",
      "schema": "./src/my-component-generator/schema.json"
    }
  }
}

Itt a my-component-generator a Schematics neve, amit az ng generate paranccsal használni fogunk. A factory megadja a TypeScript fájl útvonalát és az exportált függvény nevét, ami a Schematics logikát tartalmazza. A schema pedig a bemeneti paraméterek definícióját írja le.

schema.json: A Bemeneti Paraméterek Definíciója

Ez a JSON Schema fájl határozza meg, milyen beállításokat fogadhat el a Schematics, és milyen típusúaknak kell lenniük. Ezek az opciók kerülnek majd átadásra a Schematics logikájának.

{
  "$schema": "http://json-schema.org/schema",
  "id": "MyComponentGeneratorSchema",
  "title": "My Component Generator Options",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "A generálandó komponens neve.",
      "x-prompt": "Mi legyen a komponens neve?",
      "$default": {
        "$source": "argv",
        "index": 0
      }
    },
    "path": {
      "type": "string",
      "description": "Az útvonal, ahova a komponenst generálni kell.",
      "default": "src/app",
      "format": "path"
    },
    "flat": {
      "type": "boolean",
      "description": "Ne hozzon létre külön mappát a komponensnek.",
      "default": false
    }
  },
  "required": [
    "name"
  ]
}

A properties objektumban definiálhatod az összes elfogadott opciót. Az x-prompt lehetővé teszi, hogy interaktív kérdést tegyél fel a felhasználónak, ha az opciót nem adták meg a parancssorban.

index.ts: A Szabálygyár (Rule Factory)

Ez a TypeScript fájl tartalmazza a Schematics legfontosabb logikáját. Itt definiálod azokat a szabályokat (Rules), amelyek a projekt fájlrendszerét módosítják. Minden Schematics egy factory function-ből indul, ami egy Rule-t ad vissza.

import { Rule, SchematicContext, Tree, apply, url, applyTemplates, move, mergeWith, forEach, FileEntry } from '@angular-devkit/schematics';
import { strings } from '@angular-devkit/core';

interface MyComponentGeneratorSchema {
  name: string;
  path: string;
  flat: boolean;
}

export function myComponentGenerator(options: MyComponentGeneratorSchema): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    // 1. Definiáld a template-ek forrását
    const sourceTemplates = url('./files');

    // 2. Alkalmazd a template-eket és a transzformációkat
    const transformedTemplates = apply(sourceTemplates, [
      applyTemplates({
        ...options,
        ...strings, // `classify`, `dasherize`, `camelize` stb. segédfüggvények
      }),
      move(options.flat ? options.path : `${options.path}/${strings.dasherize(options.name)}`),
      // Esetlegesen egyéb fájlkezelési logikák
    ]);

    // 3. Egyesítsd a generált fájlokat a meglévő Tree-vel
    return mergeWith(transformedTemplates)(tree, _context);
  };
}

Nézzük meg a kulcsfogalmakat:

  • Rule: Egy függvény, ami egy Tree-t fogad el, és egy új Tree-t (vagy egy Promise-t/Observable-t, ami egy Tree-vel oldódik fel) ad vissza. Ez a Schematics lényege.
  • Tree: A fájlrendszer absztrakciója. Két részből áll: a base Tree (a jelenlegi fájlrendszer állapota) és a committed Tree (a végrehajtásra váró változások). A Schematics sosem módosítja közvetlenül a fájlrendszert, hanem a Tree-n keresztül dolgozik.
  • SchematicContext: Információkat és segédfüggvényeket biztosít a Schematics végrehajtási környezetéről (pl. logolás, taskok futtatása).

Template Fájlok (pl. src/my-component-generator/files mappa)

Ezek a fájlok tartalmazzák a generálandó kód sablonjait. Speciális szintaxissal (EJS-szerű) injektálhatsz változókat és logikát.

Például egy __name__.component.ts.template fájlban:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: '',
  templateUrl: './.component.html',
  styleUrls: ['./.component.scsscss'],
})
export class Component implements OnInit {
  constructor() {}
  ngOnInit(): void {}
}

Figyeld meg a __name__ konvenciót a fájlnévben és a <%= ... %> interpolációt a tartalomban. A strings segédfüggvények (dasherize, classify) rendkívül hasznosak a névadási konvenciók betartásához.

Fájlkezelési Műveletek és Közös Segédfüggvények

A Schematics rengeteg beépített segédfüggvényt kínál a fájlrendszer manipulálásához:

  • url('./files'): Betölti a megadott útvonalon lévő template fájlokat.
  • apply(source, [rules...]): Alkalmaz egy sor Rule-t egy Source-ra (pl. egy template gyűjteményre).
  • applyTemplates({ ...options, ...strings }): Ez egy Rule, ami a template-ekben lévő változókat a megadott opciókkal és a strings segédfüggvényekkel helyettesíti.
  • move(path): Ez egy Rule, ami áthelyezi a fájlokat egy új célútvonalra.
  • mergeWith(source, strategy): Ez egy Rule, ami egyesíti a source (pl. generált fájlok) tartalmát a jelenlegi Tree-vel. A strategy (pl. OverwriteStrategy.Overwrite vagy OverwriteStrategy.AllowForbidden) meghatározza, hogyan kezelje az ütközéseket.
  • chain([rule1, rule2, ...]): Egy sor Rule láncolására szolgál, szekvenciálisan hajtja végre őket.
  • externalSchematic('collection-name', 'schematic-name', options): Lehetővé teszi, hogy egy másik Schematics-et hívj meg a sajátodból. Ez nagyon hasznos a modularitás szempontjából.

Fájlok Módosítása: Túl a Generáláson

A Schematics nem csak új fájlokat tud létrehozni, hanem meglévőket is módosíthat. Ez a legkomplexebb, de egyben legizgalmasabb része is:

  • Text alapú módosítás: Egyszerűbb esetekben, például egy package.json frissítésénél, elegendő lehet a fájl tartalmát stringként kezelni és reguláris kifejezésekkel módosítani.
  • Abstract Syntax Tree (AST) alapú módosítás: Ez a legrobosztusabb és leginkább ajánlott módszer TypeScript fájlok módosítására. Az @schematics/angular számos segédfüggvényt biztosít ehhez, például:
    • getSourceFile(tree, path): Betölti egy TypeScript fájl AST-jét.
    • addDeclarationToModule(source, path, declarationName, modulePath): Hozzáad egy komponenst/direktívát egy modulhoz.
    • addImportToModule(), addExportToModule(): Hasonlóan importok és exportok hozzáadására.
    • A Change interfész és a InsertChange, ReplaceChange osztályok segítségével precízen tudsz módosításokat végrehajtani a kódban.

    Az AST módosításokhoz mélyebb ismeret szükséges a TypeScript compiler API-ról, de a pontosságuk és a refaktorálási képességeik páratlanok.

Példa: Egy Egyszerű Komponens Generátor

Vegyük a fentebb bemutatott példát, és nézzük meg, hogyan áll össze egy egyszerű komponensgenerátor:

  1. src/my-component-generator/index.ts:

    import { Rule, SchematicContext, Tree, apply, url, applyTemplates, move, mergeWith } from '@angular-devkit/schematics';
    import { strings } from '@angular-devkit/core';
    
    interface ComponentOptions {
      name: string;
      path: string;
      flat: boolean;
      style: 'css' | 'scss' | 'less' | 'none'; // Kiegészítjük a stílus opcióval
    }
    
    export function myComponentGenerator(options: ComponentOptions): Rule {
      return (tree: Tree, _context: SchematicContext) => {
        // 1. Alapértelmezett értékek kezelése, pl. ha nincs megadva útvonal
        options.path = options.path || 'src/app';
        options.style = options.style || 'css'; // Alapértelmezett stílus
    
        const templateSource = apply(url('./files'), [
          applyTemplates({
            ...options,
            ...strings,
          }),
          move(options.flat ? options.path : `${options.path}/${strings.dasherize(options.name)}`),
        ]);
    
        // Opcionálisan: frissítsük az Angular modult, hogy regisztráljuk a komponenst
        // Ehhez bonyolultabb AST manipulációra lenne szükség, ami meghaladja
        // ennek a példának a kereteit. Használhatjuk az @schematics/angular
        // beépített segédjeit, mint az addDeclarationToModule.
    
        return mergeWith(templateSource)(tree, _context);
      };
    }
    
  2. src/my-component-generator/schema.json:

    {
      "$schema": "http://json-schema.org/schema",
      "id": "MyComponentGeneratorSchema",
      "title": "My Component Generator Options",
      "type": "object",
      "properties": {
        "name": { "type": "string", "description": "A komponens neve.", "x-prompt": "Mi legyen a komponens neve?", "$default": { "$source": "argv", "index": 0 } },
        "path": { "type": "string", "description": "A komponens útvonala.", "default": "src/app", "format": "path" },
        "flat": { "type": "boolean", "description": "Ne hozzon létre külön mappát.", "default": false },
        "style": { "type": "string", "description": "A stílus formátuma.", "enum": ["css", "scss", "less", "none"], "default": "css" }
      },
      "required": ["name"]
    }
    
  3. src/my-component-generator/files/__name__.component.ts.template:

    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: '',
      templateUrl: './.component.html',
      styleUrls: ['./.component.scsscss'],
    })
    export class Component implements OnInit {
      constructor() {}
      ngOnInit(): void {}
    }
    
  4. src/my-component-generator/files/__name__.component.html.template:

    
    <p><%= dasherize(name) %> works!</p>
    
  5. src/my-component-generator/files/__name__.component.scsscss.template:

    /* Component styles for <%= dasherize(name) %> */
    :host {
      display: block;
    }
    

Ezután futtathatod a Schematics-edet az Nx workspace-ben:

nx generate my-schematics:my-component-generator my-new-component --path=src/app/features --style=scss

Vagy standalone projektben (először buildeld a Schematics-et, majd linkeld, vagy futtasd direktben a schematics paranccsal, ha éppen fejleszted):

npm run build
schematics ./:my-component-generator my-new-component --path=src/app/features --style=scss

Schematics Tesztelése

A Schematics tesztelése elengedhetetlen a megbízható működéshez. A @angular-devkit/schematics/testing modul eszközöket biztosít ehhez:

import { SchematicTestRunner, RunSchematicOptions } from '@angular-devkit/schematics/testing';
import * as path from 'path';

const collectionPath = path.join(__dirname, '../collection.json');

describe('my-component-generator', () => {
  const runner = new SchematicTestRunner('schematics', collectionPath);

  it('generates files for a new component', async () => {
    const tree = await runner.runSchematicAsync('my-component-generator', { name: 'test-comp', path: 'src/app' }, Tree.empty());
    expect(tree.files.length).toBe(3); // .ts, .html, .css (vagy scss)
    expect(tree.files).toEqual(expect.arrayContaining([
      '/src/app/test-comp/test-comp.component.ts',
      '/src/app/test-comp/test-comp.component.html',
      '/src/app/test-comp/test-comp.component.css',
    ]));
  });

  it('generates flat component', async () => {
    const tree = await runner.runSchematicAsync('my-component-generator', { name: 'flat-comp', path: 'src/app', flat: true }, Tree.empty());
    expect(tree.files).toEqual(expect.arrayContaining([
      '/src/app/flat-comp.component.ts',
      '/src/app/flat-comp.component.html',
      '/src/app/flat-comp.component.css',
    ]));
  });
});

A SchematicTestRunner segítségével futtathatod a Schematics-edet egy virtuális Tree-n, és ellenőrizheted a generált fájlokat és azok tartalmát.

Publikálás és Használat

Miután elkészültél és letesztelted a Schematics-edet, itt az ideje használni:

  • Helyi használat: Ha egy Angular projektben dolgozol, és a Schematics-ed is a projekt része (pl. egy Nx könyvtárban), akkor az ng generate parancs automatikusan megtalálja. Pl.: ng generate my-lib:my-component-generator.
  • npm publikálás: A Schematics csomagokat is publikálhatod az npm-re. A package.json-ben meg kell adnod a schematics mezőt, ami a collection.json-re mutat.
    {
      "name": "my-custom-schematics",
      "version": "1.0.0",
      "description": "My custom Angular Schematics",
      "schematics": "./collection.json",
      "private": false,
      "scripts": {
        "build": "tsc -p tsconfig.json"
      }
    }
    

    Ezután egyszerűen futtasd az npm publish parancsot (ügyelj a verziószámra!). Mások ezután telepíthetik a csomagodat (npm install my-custom-schematics), és használhatják a Schematics-edet: ng generate my-custom-schematics:my-component-generator.

Gyakorlati Tippek és Bevált Módszerek

  • Kezdj kicsiben: Ne próbálj azonnal komplex Schematics-et írni. Kezdj egyszerű fájlgenerálással, majd fokozatosan építsd tovább.
  • Használd ki az Angular CLI Schematics-eit: Ha valamilyen alap Angular funkciót szeretnél módosítani, vagy csak annak működését akarod reprodukálni, nézz bele az @schematics/angular forráskódjába. Rengeteg hasznos példát és segédfüggvényt találsz ott.
  • Moduláris felépítés: Bonyolultabb Schematics-ek esetén érdemes a logikát kisebb, újrafelhasználható Rule-okra bontani. A chain és az externalSchematic segít ebben.
  • Hibakezelés és validáció: Mindig validáld a bejövő opciókat a schema.json-ben és a Schematics logikájában. Kezeld a lehetséges hibákat, és adj értelmes visszajelzést a felhasználónak.
  • Dokumentáció: Dokumentáld a Schematics-edet! Írd le, mire való, milyen opciókat fogad el, és hogyan kell használni.
  • Tesztelés, tesztelés, tesztelés: Ahogy a komplexitás növekszik, a tesztek válnak a megbízhatóság alapjává.

Összefoglalás

Az Angular Schematics egy game-changer eszköz a fejlesztés automatizálásában és a csapatok közötti konzisztencia biztosításában. Bár a kezdeti tanulási görbe meredeknek tűnhet az AST manipuláció és a Tree fogalmának megértése miatt, a befektetett idő megtérül a hosszú távú hatékonyság és a jobb kódminőség formájában.

Kezdj el kísérletezni, automatizáld a repetitív feladatokat, és emeld magasabb szintre az Angular fejlesztésedet! Ne feledd, a cél az, hogy a gépek a gépi munkát végezzék, te pedig a kreatív problémamegoldásra koncentrálj.

Remélem, ez a részletes útmutató segít neked abban, hogy magabiztosan vágj bele a saját Angular Schematics-ed elkészítésébe. Jó kódolást!

Leave a Reply

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük