$30 off During Our Annual Pro Sale. View Details »

Angular in Practice: Scaffolding mit Nx Workspace Generators

Angular in Practice: Scaffolding mit Nx Workspace Generators

Manche Entwickler müssen in Ihren Angular-Workspaces wiederkehrende Aufgaben erledigen: Etwa die Neuanlage gleichartiger Projekte, Bibliotheken sowie Komponenten oder aber die Sicherstellung einer bestimmten Ordnerstruktur. Während im Angular-CLI-Umfeld hierfür typischerweise Schematics zum Einsatz kommen, führt Nrwl bei Nx-Workspaces ein eigenes Konzept ins Feld: Workspace Generators.

Christian Liebel von Thinktecture zeigt Ihnen die grundlegenden Konzepte dieser Generatoren, wie sie sich von Schematics abgrenzen und welche Techniken man für die Implementierung beherrschen muss.

Christian Liebel
PRO

October 18, 2022
Tweet

More Decks by Christian Liebel

Other Decks in Programming

Transcript

  1. Angular in Practice Scaffolding mit Nx Workspace Generators Christian Liebel

    @christianliebel christian.liebel@thinktecture.com
  2. Hello, it’s me. Angular in Practice Scaffolding mit Nx Workspace

    Generators Christian Liebel Twitter: @christianliebel Email: christian.liebel @thinktecture.com Angular & PWA Slides: thinktecture.com /christian-liebel
  3. Things NOT to expect - Pretty code - A super

    polished workshop— this is its debut :) Things To Expect - Basic concepts - Nx workspace generators vs. Angular CLI schematics - Patterns & practices - 12 Hands-on exercises Scaffolding mit Nx Workspace Generators Angular in Practice
  4. 09:00–10:30 Block 1 10:30–11:00 Break 11:00–12:30 Block 2 Angular in

    Practice Scaffolding mit Nx Workspace Generators Timetable
  5. Setup complete? (Code editor, Node.js 16+, Git) Setup (1/2) Scaffolding

    mit Nx Workspace Generators Angular in Practice
  6. git clone https://github.com/thinktecture/ angular-days-fall-2022-gen.git cd angular-days-fall-2022-gen npm i Angular in

    Practice Scaffolding mit Nx Workspace Generators Setup (2/2) LAB #0
  7. Nx Introduction Generator Concepts Generator Techniques Summary Agenda Scaffolding mit

    Nx Workspace Generators Angular in Practice
  8. Nx Introduction Generator Concepts Generator Techniques Summary Agenda Scaffolding mit

    Nx Workspace Generators Angular in Practice
  9. What is Nx? – “Build system with first class monorepo

    support and powerful integrations” (nx.dev) – Similar to Angular CLI, but far more advanced – Nx workspaces can host frontends and backends based on various frameworks – Used by Storybook, Microsoft Fluent UI, Babylon.js, and many of our customers – Nrwl team was founded by former Angular team members Nx Introduction Scaffolding mit Nx Workspace Generators Angular in Practice
  10. npx create-nx-workspace@latest [my-workspace] Angular in Practice Scaffolding mit Nx Workspace

    Generators Generating a new Nx workspace
  11. Angular in Practice Scaffolding mit Nx Workspace Generators Generating a

    new Nx workspace
  12. Generators – Similar to Angular Schematics – Generators provide a

    way to automate many tasks you regularly perform as part of your development workflow. – Whether it is scaffolding out components, features, ensuring libraries are generated and structured in a certain way, or updating your configuration files, generators help you standardize these tasks in a consistent, and predictable manner. (https://nx.dev/generators/using-schematics) Nx Introduction Scaffolding mit Nx Workspace Generators Angular in Practice
  13. Generator Types Type Plugin Generators Available when an Nx plugin

    has been installed in your workspace nx generate [plugin]:[generator-name] [options] Workspace Generators Generated for your own workspace. Allows codifying processes unique to your own organization or project. Update Generators Invoked by Nx plugins when updating Nx to keep your config files in sync with the latest versions of third-party tools Nx Introduction Scaffolding mit Nx Workspace Generators Angular in Practice https://nx.dev/generators/using-schematics
  14. Devkit Angular Devkit Schematics Builders Nx Introduction Scaffolding mit Nx

    Workspace Generators Angular in Practice Nrwl Devkit Generators Executors
  15. Generators vs. Schematics Generators – Based on Promises – Directly

    perform actions – Easier to debug and test – Convenient helper methods – Supports Nx generators & Angular CLI schematics Schematics – Based on RxJS – Return rules (executed later) – Hard to debug and test – Fewer helper methods – Only supports Angular CLI schematics Angular in Practice Scaffolding mit Nx Workspace Generators Nx Introduction https://nx.dev/guides/nx-devkit-angular-devkit
  16. Nx Introduction Generator Concepts Generator Techniques Summary Agenda Scaffolding mit

    Nx Workspace Generators Angular in Practice
  17. npx nx generate workspace-generator my-generator Angular in Practice Scaffolding mit

    Nx Workspace Generators Generating Generators with A Generator LAB #1
  18. import { Tree, formatFiles, installPackagesTask } from '@nrwl/devkit'; import {

    libraryGenerator } from '@nrwl/workspace/generators'; export default async function (tree: Tree, schema: any) { await libraryGenerator(tree, { name: schema.name }); await formatFiles(tree); return () => { installPackagesTask(tree); }; } Generator Concepts Scaffolding mit Nx Workspace Generators Angular in Practice
  19. Tree – The tree represents a virtual file system –

    It contains all files that already existed in the real file system – All generator actions are executed against the tree, but are only added to the staging area – If all actions complete successfully, the staging area is written to the filesystem – In case of an error, it is simply discarded Generator Concepts Scaffolding mit Nx Workspace Generators Angular in Practice
  20. JSON Schema – Defines the generator inputs – Allows specifying

    required/optional properties, defaults (e.g., read from argv), allowed values (enums), … – Used by the CLI to map the user’s inputs to a schema object – The CLI may show an interactive prompt (x-prompt, e.g. for style sheet flavor selection) – Be careful: You may also want to provide a TypeScript interface for the resulting object so your generator can be called from another one (any by default, can be generated from the JSON schema) Generator Concepts Scaffolding mit Nx Workspace Generators Angular in Practice
  21. JSON Schema Example: https://github.com/nrwl/nx/blob/master/packages/angular/src/generato rs/application/schema.json Generator Concepts Scaffolding mit Nx

    Workspace Generators Angular in Practice
  22. Nx Introduction Generator Concepts Generator Techniques Summary Agenda Scaffolding mit

    Nx Workspace Generators Angular in Practice
  23. Demo Path 1. Create a common Angular library 2. Create

    a new login application a) Invoke application generator b) Generate common files c) Import common library 3. Create a new login step a) Invoke Angular schematic b) Use Typescript AST to update other file Angular in Practice Scaffolding mit Nx Workspace Generators Generator Techniques
  24. npx nx g @nrwl/angular:library my-library Angular in Practice Scaffolding mit

    Nx Workspace Generators Generating a new Angular library LAB #2
  25. Typed schema { "$schema": "http://json-schema.org/schema", "cli": "nx", "$id": "my-generator", "type":

    "object", "properties": { "name": { "type": "string", "description": "Library name", "$default": { "$source": "argv", "index": 0 } } }, "required": ["name"] } Update my-generator/index.ts as follows: import { Tree } from '@nrwl/devkit'; interface Schema { name: string; } export default async function (tree: Tree, schema: Schema) { } Angular in Practice Scaffolding mit Nx Workspace Generators Generator Techniques LAB #3
  26. Invoking other generators // New imports import { applicationGenerator }

    from '@nrwl/angular/generators'; // Update code export default async function (tree: Tree, schema: Schema) { await applicationGenerator(tree, { name: schema.name, routing: true, prefix: schema.name, style: 'scss', }); } à Test your generator: nx workspace-generator my-generator app --dry-run Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #4
  27. Generate files generateFiles(tree, sourceDir, destinationDir, {}); Does not work for

    empty directories! Tip: Create .gitkeep files for that, and remove them with another generator later! Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  28. Utility functions The @nrwl/devkit package provides many utility functions that

    can be used in schematics to help with modifying files, reading and updating configuration files, and working with an Abstract Syntax Tree (AST). applyChangesToString() joinPathFragments() readProjectConfiguration() formatFiles() installPackagesTask() readJson() Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  29. Generate files 1. Create my-generator/files/app.config.ts: export const loginSteps = [];

    2. Adjust your workspace generator as follows: // New imports import {generateFiles, joinPathFragments, readProjectConfiguration, Tree} from '@nrwl/devkit'; // Add code const sourceDir = joinPathFragments(__dirname, 'files'); const projectDir = readProjectConfiguration(tree, schema.name).root; const destinationDir = joinPathFragments(projectDir, 'src', 'app'); generateFiles(tree, sourceDir, destinationDir, {}); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #5
  30. Generate files 3. Test your generator: nx workspace-generator my-generator app

    --dry-run Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #5
  31. Generate files Be careful! All TypeScript files in the generator

    folder are compiled, that’s why transpiled files and source maps are written to the output: Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  32. Generate files 1. Rename files/app.config.ts to files/app.config.ts__tpl__ 2. Adjust your

    workspace generator as follows: generateFiles(tree, sourceDir, destinationDir, { tpl: '' }); 3. Test your generator: nx workspace-generator my-generator app --dry-run Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #6
  33. Generate files – Utils import { classify, dasherize } from

    '@nrwl/workspace/src/utils/strings'; Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice Type Input myCoolApp classify() MyCoolApp dasherize() my-cool-app
  34. Generate files – Substitutions generateFiles(tree, sourceDir, destinationDir, { tpl: '',

    projectFileName: dasherize(schema.name), projectClassName: classify(schema.name), }); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  35. High-level utility functions (@nrwl/angular) import { insertNgModuleImport } from '@nrwl/angular/src/generators/utils';

    insertNgModuleImport(tree, modulePath, moduleToAdd); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  36. Writing a custom utility function Introduce a new file generators/utils/insert-import.ts:

    import { Tree } from '@nrwl/devkit'; import { createSourceFile, ScriptTarget } from 'typescript'; import { insertImport as nxInsertImport } from '@nrwl/workspace/src/utilities/ast-utils'; export function insertImport(tree: Tree, filePath: string, symbolName: string, fileName: string) { const moduleSrc = tree.read(filePath, 'utf-8'); const sourceFile = createSourceFile(filePath, moduleSrc, ScriptTarget.Latest, true); nxInsertImport(tree, sourceFile, filePath, symbolName, fileName); } Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #7
  37. High-level utility functions Adjust your workspace generator as follows: //

    New imports import {insertNgModuleImport} from "@nrwl/angular/src/generators/utils"; import {insertImport} from "../utils/insert-import"; // Add at the end const modulePath = joinPathFragments(projectDir, 'src', 'app', 'app.module.ts'); insertImport(tree, modulePath, 'MyLibraryModule', '@my-workspace/my- library'); insertNgModuleImport(tree, modulePath, 'MyLibraryModule'); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #8
  38. High-level utility functions Run your generator: nx workspace-generator my-generator app

    Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #8
  39. Invoking Angular schematics import { wrapAngularDevkitSchematic } from 'nx/src/commands/ngcli-adapter'; wrapAngularDevkitSchematic('@schematics/angular',

    'module'); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  40. Invoking Angular schematics 1. Generate a new generator: nx generate

    workspace-generator my-step 2. Adjust the schema to include a project property: "project": { "type": "string", "description": "Target project" } 3. Add the following interface to the generators/my-step/index.ts file: interface Schema { name: string; project: string; } Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #9
  41. Invoking Angular schematics 4. Adjust the generator to call the

    Angular module schematic: // New imports import { wrapAngularDevkitSchematic } from "nx/src/adapter/ngcli-adapter"; import { dasherize } from "@nrwl/workspace/src/utils/strings"; // New code export default async function (tree: Tree, schema: Schema) { const moduleSchematic = wrapAngularDevkitSchematic('@schematics/angular', 'module'); await moduleSchematic(tree, { name: dasherize(schema.name), project: schema.project }); } Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #9
  42. Invoking Angular schematics 5. Test your generator: nx workspace-generator my-step

    step --project app --dry-run Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #9
  43. String replacements fileContents.replace( /(title.*?;)/g, `constructor(public readonly loginService: LoginService) { this.loginService.initialize();

    }` ); Note: This is very simple, but may lead to unintended side effects. Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  44. TypeScript AST TypeScript package allows parsing TS source files to

    an abstract syntax tree (AST) and traverse it, which is a safer approach compared to string replacements createSourceFile() isIdentifier() isVariableStatement() Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice
  45. TypeScript AST // New imports import {ChangeType, joinPathFragments, readProjectConfiguration, Tree}

    from '@nrwl/devkit'; Import {createSourceFile, ScriptTarget} from 'typescript'; // Add code const projectDir = readProjectConfiguration(tree, schema.project).root; const configFileName = joinPathFragments(projectDir, 'src', 'app', 'app.config.ts'); const fileContents = tree.read(configFileName).toString('utf-8'); const sourceFile = createSourceFile(configFileName, fileContents, ScriptTarget.Latest); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #10
  46. TypeScript AST // New imports import {createSourceFile, Identifier, isIdentifier, isVariableStatement,

    ScriptTarget} from "typescript"; // Add code const stepsDeclaration = sourceFile.statements .filter(isVariableStatement) .map(s => s.declarationList.declarations[0]) .filter(d => isIdentifier(d.name)) .find(d => (d.name as Identifier).escapedText === 'loginSteps'); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #10
  47. TypeScript AST // New imports import {applyChangesToString, ChangeType, joinPathFragments, readProjectConfiguration,

    Tree} from '@nrwl/devkit'; // Add code const stepToAdd = `"${classify(schema.name)}",`; const newContents = applyChangesToString(fileContents, [{ type: ChangeType.Insert, index: stepsDeclaration.end - 1, text: stepToAdd }]); tree.write(configFileName, newContents); Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #10
  48. TypeScript AST Run your generator: nx workspace-generator my-step step --project

    app Generator Techniques Scaffolding mit Nx Workspace Generators Angular in Practice LAB #10
  49. import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; describe('app', () => {

    let appTree: Tree; beforeEach(() => { appTree = createTreeWithEmptyWorkspace(); }); it('…', () => {}); }); Testing Scaffolding mit Nx Workspace Generators Angular in Practice LAB #11
  50. Visual Studio Code Command Palette Debug: Create JavaScript Debug Terminal

    WebStorm/IntelliJ IDEA Debug npm run script Tip: --dry-run does not write any updates to the file system Debugging Scaffolding mit Nx Workspace Generators Angular in Practice LAB #12
  51. Nx Introduction Generator Concepts Generator Techniques Summary Agenda Scaffolding mit

    Nx Workspace Generators Angular in Practice
  52. Trade-off Implementing a generalized/dynamic solution takes time! Always consider how

    much time you can save by using workspace generators. In one of our projects, implementing the generators took as much time as creating a new application from scratch, so the investment quickly paid off. Summary Scaffolding mit Nx Workspace Generators Angular in Practice
  53. Documentation Generator/schematics documentation isn’t great Reading the code from Nx’s

    own generators was more helpful https://github.com/nrwl/nx/tree/master/packages/angular/src/generators Entirely different programming field Defensive programming style recommended (including meaningful error messages) Summary Scaffolding mit Nx Workspace Generators Angular in Practice
  54. Documentation Not all handy functions are exported by @nrwl/devkit. Indeed

    better than Angular CLI, but requires customers to use Nx Part of Nrwl’s DNA Generators are fun, but complicated, still it may pay off! Summary Scaffolding mit Nx Workspace Generators Angular in Practice
  55. Q&A Scaffolding mit Nx Workspace Generators Angular in Practice

  56. Thank you for your kind attention! Christian Liebel @christianliebel christian.liebel@thinktecture.com