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

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
    [email protected]

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  10. npx [email protected] [my-workspace]
    Angular in Practice
    Scaffolding mit Nx Workspace Generators
    Generating a new Nx workspace

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  55. Q&A
    Scaffolding mit Nx Workspace Generators
    Angular in Practice

    View Slide

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

    View Slide