Slide 1

Slide 1 text

Angular in Practice Scaffolding mit Nx Workspace Generators Christian Liebel @christianliebel [email protected]

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

npx create-nx-workspace@latest [my-workspace] Angular in Practice Scaffolding mit Nx Workspace Generators Generating a new Nx workspace

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Devkit Angular Devkit Schematics Builders Nx Introduction Scaffolding mit Nx Workspace Generators Angular in Practice Nrwl Devkit Generators Executors

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

npx nx generate workspace-generator my-generator Angular in Practice Scaffolding mit Nx Workspace Generators Generating Generators with A Generator LAB #1

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

npx nx g @nrwl/angular:library my-library Angular in Practice Scaffolding mit Nx Workspace Generators Generating a new Angular library LAB #2

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

Q&A Scaffolding mit Nx Workspace Generators Angular in Practice

Slide 56

Slide 56 text

Thank you for your kind attention! Christian Liebel @christianliebel [email protected]