Schematics to nie tylko tworzenie plików w katalogu app/src. Jeśli chcemy w pełni skorzystać z tego narzędzia to na pewno warto wiedzieć, jak zautomatyzować wpisy do package.json a następnie automatycznie zainstowalować paczki.
Napiszmy Schematic, który pozwoli nam wybrać framework UI: Bootstrap, Skeleton lub Foundation. Następnie doda odpowiedni wpis do package.json i zainstaluje zależności.
Zaczynamy od nowego Schematic:
1 |
schematics blank ui-framework |
W powstałym katalogu tworzymy plik schema.json z prompterem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
{ "$schema": "http://json-schema.org/schema", "id": "uiframework", "title": "adds a framework to package.json", "type": "object", "properties": { "uiFramework": { "type": "string", "description": "Specifies UI framework", "x-prompt": { "message": "Which UI Framework would you like to use?", "type": "list", "items": [ { "value": "bootstrap", "label": "Bootstrap 4" }, { "value": "foundation", "label": "Foundation" }, { "value": "skeleton", "label": "Skeleton" } ] } } }, "additionalProperties": false } |
Pamiętaj o dodaniu pliku schema.json do collection.json! Inaczej prompter się nie pojawi. Właśnie spędziłem 10 minut, głowiąc się, czemu prompter się nie pojawia 🙂
Teraz tworzymy utilsa, dodającego wpis do package.json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import { Tree } from '@angular-devkit/schematics'; // helper sortujacy alfabetycznie klucze function sortObjectByKeys(obj: any) { return Object.keys(obj).sort().reduce((result, key) => (result[key] = obj[key]) && result, {} as any); } export function addPackageToPackageJson(tree: Tree, pkg: string, version: string): Tree { // sprawdzamy czy plik package.json istnieje if (tree.exists('package.json')) { // sprowadzamy do stringa treść const sourceText = tree.read('package.json')!.toString(); // sprowadzamy do obiektu const json = JSON.parse(sourceText); if (!json.dependencies) { json.dependencies = {}; } if (!json.dependencies[pkg]) { // dodajemy wpis, klucz to nazwa paczki, wartość to wersja json.dependencies[pkg] = version; // sortujemy alfabetycznie json.dependencies = sortObjectByKeys(json.dependencies); } // nadpisujemy nowa wartością, wartość '2' jako 3 parametr, zapewnia nam dobre wcięcia tree.overwrite('package.json', JSON.stringify(json, null, 2)); } return tree; } |
Rule Factory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { addPackageToPackageJson } from '../utils/add-package-json'; export interface FrameworkMap { [frameworkName: string]: string; } export function uiFramework(options: {uiFramework: string}): Rule { return (tree: Tree, context: SchematicContext) => { // klucze muszą być zgodne z podanymi value w schema.json const uiFrameworkVersionMap: FrameworkMap = { bootstrap: '4.1.3', skeleton: '3.1.6', foundation: '6.5.1', }; // do utilsa wpuszczamy drzewko reprezentujące pliki addPackageToPackageJson( tree, options.uiFramework, // opcja wybrana przez usera `~${uiFrameworkVersionMap[options.uiFramework]}` // zczytanie wersji dla wartości ); context.addTask(new NodePackageInstallTask()); }; } |
Nowe funkcjonalności:
- context.addTask(…) – metoda kontekstu, umożliwiająca wykonanie jakiegoś efektu ubocznego, np. instalacji zależności node, puszczenie lintera, co będzie taskiem zależy wyłącznie od nas
- NodePackageInstallTask() – boilerplate ze Schematics, odpowidający za uruchomienie npm install
Teraz wystarczy uruchomić Schematic i cieszyć się dodaniem wpisu i automatyczną instalacją paczki:
1 |
ng g angularlove:ui-framework |
Efekt:
Kod:
https://github.com/tomasznastaly/angular-schematics-tutorial/pull/10/files
TIP:
Do NodePackageInstallTask() można przekazać obiekt konfiguracyjny NodePackageInstallTaskOptions i wskazać w nim inny manager paczek (chociażby Yarn):
1 2 3 4 5 6 |
export class NodePackageInstallTaskOptions { packageManager: string; packageName: string; workingDirectory: string; quiet: boolean; } |