Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular in Practice: Scaffolding mit Nx Workspa...

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

October 18, 2022
Tweet

More Decks by Christian Liebel

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. Setup complete? (Code editor, Node.js 16+, Git) Setup (1/2) Scaffolding

    mit Nx Workspace Generators Angular in Practice
  5. 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
  6. 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
  7. 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
  8. Devkit Angular Devkit Schematics Builders Nx Introduction Scaffolding mit Nx

    Workspace Generators Angular in Practice Nrwl Devkit Generators Executors
  9. 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
  10. npx nx generate workspace-generator my-generator Angular in Practice Scaffolding mit

    Nx Workspace Generators Generating Generators with A Generator LAB #1
  11. 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
  12. 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
  13. 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
  14. 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
  15. npx nx g @nrwl/angular:library my-library Angular in Practice Scaffolding mit

    Nx Workspace Generators Generating a new Angular library LAB #2
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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