Slide 1

Slide 1 text

@yourtwitter Internals of the Angular CLI Minko Gechev twitter.com/mgechev github.com/mgechev blog.mgechev.com

Slide 2

Slide 2 text

twitter.com/mgechev

Slide 3

Slide 3 text

@mgechev

Slide 4

Slide 4 text

@mgechev Framework

Slide 5

Slide 5 text

@mgechev Framework CLI Components

Slide 6

Slide 6 text

@mgechev Framework CLI Components I18n Language service Router Animations Forms PWA HTTP More!

Slide 7

Slide 7 text

@mgechev Framework CLI Components I18n Language service Router Animations Forms PWA HTTP More!

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

@mgechev Disclaimer You don’t need to know any of the following content to use the Angular CLI

Slide 10

Slide 10 text

@mgechev Framework CLI Components I18n Language service Router Animations Forms PWA HTTP More!

Slide 11

Slide 11 text

@mgechev Framework CLI Components I18n Language service Router Animations Forms PWA HTTP More!

Slide 12

Slide 12 text

@mgechev @angular/cli

Slide 13

Slide 13 text

@mgechev @angular/cli Builders Schematics

Slide 14

Slide 14 text

@mgechev @angular/cli Builders Schematics Jobs Footprints ng add ng update

Slide 15

Slide 15 text

@mgechev @angular/cli Builders Schematics Jobs Footprints ng add ng update webpack

Slide 16

Slide 16 text

@mgechev @angular/cli Builders Schematics Jobs Footprints ng add ng update webpack TypeScript terser

Slide 17

Slide 17 text

@mgechev @angular/cli Builders Schematics Jobs Footprints ng add ng update ngc webpack TypeScript terser

Slide 18

Slide 18 text

@mgechev @angular/cli Builders Schematics Jobs Footprints ng add ng update ngc webpack TypeScript terser

Slide 19

Slide 19 text

twitter.com/ mgechev

Slide 20

Slide 20 text

@yourtwitter $ ng new sample --collection=custom Creating a project

Slide 21

Slide 21 text

@mgechev Creating a project ● Reading schematics collection from disk ● Creating an in-memory file system ● Transformation of the files (placeholder replacement) ● Committing files to disk

Slide 22

Slide 22 text

@mgechev

Slide 23

Slide 23 text

@mgechev

Slide 24

Slide 24 text

@mgechev

Slide 25

Slide 25 text

@mgechev Schematics could be used for… Scaffolding Updates NgAdd

Slide 26

Slide 26 text

@mgechev Scaffolding Updates NgAdd Code transform

Slide 27

Slide 27 text

@mgechev Scaffolding Updates NgAdd Code transform

Slide 28

Slide 28 text

@mgechev Code transformation ● Direct string replacements ● Regular expressions ● AST transformation

Slide 29

Slide 29 text

String replacements @Component({ selector: '<%= selector %>' })

Slide 30

Slide 30 text

String replacements @Component({ selector: '<%= selector %>' }) fileContent.replace('<%= selector %>', 'selector');

Slide 31

Slide 31 text

String replacements @Component({ selector: '<%= selector %>' }) fileContent.replace('<%= selector %>', 'selector'); @Component({ selector: 'selector' })

Slide 32

Slide 32 text

String replacements @Component({ selector: '<%= selector %>' }) fileContent.replace('<%= selector %>', 'selector'); @Component({ selector: 'selector' })

Slide 33

Slide 33 text

Regex replacements @Component({ selector: '<%= selector %>' })

Slide 34

Slide 34 text

Regex replacements @Component({ selector: '<%= selector %>' }) const data = {selector: 'app-component'}; fileContent.replace(/<%= (\w+) %>/, function(a, m) { return data[m]; });

Slide 35

Slide 35 text

Regex replacements @Component({ selector: '<%= selector %>' }) @Component({ selector: 'app-component' }) const data = {selector: 'app-component'}; fileContent.replace(/<%= (\w+) %>/, function(a, m) { return data[m]; });

Slide 36

Slide 36 text

Regex replacements @Component({ selector: '<%= selector %>' }) @Component({ selector: 'app-component' }) const data = {selector: 'app-component'}; fileContent.replace(/<%= (\w+) %>/, function(a, m) { return data[m]; });

Slide 37

Slide 37 text

Regex replacements @ViewChild('foo') foo; @ViewChild('foo', { static: true }) foo; ❓

Slide 38

Slide 38 text

@mgechev Conditions ● Should be a property decorator ● The class should be a component ● * Should be a symbol from Angular

Slide 39

Slide 39 text

Regex replacements @ViewChild('foo') foo; @ViewChild('foo', { static: true }) foo; ❌

Slide 40

Slide 40 text

@mgechev Source code + Grammar

Slide 41

Slide 41 text

@mgechev Source code + Grammar

Slide 42

Slide 42 text

twitter.com/mgechev

Slide 43

Slide 43 text

twitter.com/mgechev

Slide 44

Slide 44 text

twitter.com/mgechev

Slide 45

Slide 45 text

@mgechev

Slide 46

Slide 46 text

twitter.com/mgechev

Slide 47

Slide 47 text

@mgechev Introduction to compilers

Slide 48

Slide 48 text

@mgechev Introduction to compilers …in 2-ish minutes

Slide 49

Slide 49 text

@mgechev Disclaimer You don’t need to understand compilers to use the Angular CLI

Slide 50

Slide 50 text

@mgechev COMPILER Input Output

Slide 51

Slide 51 text

@mgechev COMPILER FRONT-END BACK-END Input Output

Slide 52

Slide 52 text

@mgechev Input Output FRONT-END

Slide 53

Slide 53 text

@mgechev FRONT-END LEXICAL ANALYSIS SYNTAX ANALYSIS … Input Output …

Slide 54

Slide 54 text

@mgechev const product = a * b;

Slide 55

Slide 55 text

@mgechev tokenize('const product = a * b;')

Slide 56

Slide 56 text

@mgechev tokenize('const product = a * b;') [ { lexeme: 'const', type: 'keyword' }, { lexeme: 'product', type: 'identifier' }, { lexeme: '=', type: 'operator' }, ... ]

Slide 57

Slide 57 text

@mgechev parse(tokenize('...'))

Slide 58

Slide 58 text

@mgechev parse(tokenize('...')) CONST PRODUCT * A B

Slide 59

Slide 59 text

@mgechev AST CONST PRODUCT * A B parse(tokenize('...'))

Slide 60

Slide 60 text

@Component({ selector: 'app-component', template: '...' }) class AppComponent { ... } CLASS DECORATOR OBJ LITERAL SELECTOR APP- COMPONENT TEMPLATE ‘…’ PROPERTY[0] PROPERTY[1]

Slide 61

Slide 61 text

@mgechev Conditions ● Should be a property decorator ● The class should be a component ● * Should be a symbol from Angular

Slide 62

Slide 62 text

visitClass(node) { node.decorators.forEach(visitDecorator); } visitDecorator(node) { if (node.name === 'Component') { visitComponent(node.parent) } } visitComponent(class) { class.properties.forEach(prop => { if (prop.decorator && prop.decorator.name === 'ViewChild') { // } }); } AST visitor

Slide 63

Slide 63 text

visitClass(node) { node.decorators.forEach(visitDecorator); } visitDecorator(node) { if (node.name === 'Component') { visitComponent(node.parent) } } visitComponent(class) { class.properties.forEach(prop => { if (prop.decorator && prop.decorator.name === 'ViewChild') { // } }); } Property decorator Should be inside a component AST visitor

Slide 64

Slide 64 text

visitClass(node) { node.decorators.forEach(visitDecorator); } visitDecorator(node) { if (node.name === 'Component') { visitComponent(node.parent) } } visitComponent(class) { class.properties.forEach(prop => { if (prop.decorator && prop.decorator.name === 'ViewChild') { // } }); } Property decorator Should be inside a component AST visitor

Slide 65

Slide 65 text

Property decorator Should be inside a component visitClass(node) { node.decorators.forEach(visitDecorator); } visitDecorator(node) { if (node.name === 'Component') { visitComponent(node.parent) } } visitComponent(class) { class.properties.forEach(prop => { if (prop.decorator && prop.decorator.name === 'ViewChild') { // } }); } AST visitor

Slide 66

Slide 66 text

@mgechev Conditions ● Should be a property decorator ● The class should be a component ● * Should be a symbol from Angular

Slide 67

Slide 67 text

@mgechev Source code + Grammar + Type checker

Slide 68

Slide 68 text

@mgechev Source code + Grammar + Type checker

Slide 69

Slide 69 text

@yourtwitter // Get the type checker from the program const typeChecker = program.getTypeChecker(); // Get the type of the symbol const type = typeChecker.getTypeAtLocation(node);

Slide 70

Slide 70 text

@mgechev v10 focus • Opt-in strict flag • Increase visibility

Slide 71

Slide 71 text

@mgechev v10 focus • Opt-in strict flag • Increase visibility

Slide 72

Slide 72 text

@mgechev Strict Mode Strict TypeScript type checking strictTemplates in Angular Opt-in ES5 support Opt-in CommonJS support Reduced side-effects

Slide 73

Slide 73 text

@mgechev

Slide 74

Slide 74 text

twitter.com/mgechev >15% of bugs detectable at build time

Slide 75

Slide 75 text

@yourtwitter Strictness helps in Automated updates Compile-time optimizations Angular Universal Efficient prefetching Optimized i18n pipeline …

Slide 76

Slide 76 text

@mgechev Scaffolding Updates NgAdd Code transform

Slide 77

Slide 77 text

@mgechev Developer facing APIs

Slide 78

Slide 78 text

@mgechev Scaffolding Updates NgAdd Code transform

Slide 79

Slide 79 text

{ "name": "my-package", "version": "1.0.0", "schematics": "./collection.json" } { "name": "my-package", "schematics": { "ng-add": { "factory": "./ng-add/index.js”, "schema": "ng-add/schema.json", "description": " ..." }, "ng-new": { "factory": "./ng-new/index.js", "schema": "./ng-new/schema.json", "description": " ..." } } } package.json collection.json

Slide 80

Slide 80 text

ng-add / ng-new import {Tree} from '@angular-devkit/schematics'; export default function MySchematic(options: any) { return (tree: Tree) => { tree.create(options.path + '/hi', 'Hello world!'); return tree; }; }

Slide 81

Slide 81 text

@mgechev Scaffolding Updates NgAdd Code transform

Slide 82

Slide 82 text

{ "name": "my-package", "version": "1.0.0", "ng-update": { "migrations": "migrations.json" } } { "schematics": { "v1": { "factory": "./v1", "version": "1.0", "description": " ..." }, "v2": { "factory": "./v2", "version": "2.0", "description": " ..." } } } package.json migrations.json

Slide 83

Slide 83 text

@mgechev Mid-recap ● Schematics used for code transform ● Agnostic to the Angular CLI ● Widely adopted in the open source community ● Use semantic replacements using: ● Language grammar ● Language type system

Slide 84

Slide 84 text

@mgechev Building Angular

Slide 85

Slide 85 text

@mgechev @angular/cli Builders Schematics Jobs Footprints ng add ng update ngc webpack TypeScript terser

Slide 86

Slide 86 text

@mgechev Angular CLI builders @angular/cli $ ng run [app]:[cmd] angular.json require(builder) builder execute ✨

Slide 87

Slide 87 text

@yourtwitter $ ng run app:serve # ng serve $ ng run app:build # ng build Creating a project

Slide 88

Slide 88 text

@mgechev Browser builder

Slide 89

Slide 89 text

@mgechev Aggregate js file size in KB for angular.io builds Is --prod worth it? 5000 4000 3000 2000 1000 0 658 KB 5072 KB Production Development Almost 10x smaller!

Slide 90

Slide 90 text

@mgechev ngc tsc opt wp terser tsc ngcc i18n ng-xi18n

Slide 91

Slide 91 text

@mgechev ngc tsc opt wp terser tsc

Slide 92

Slide 92 text

@mgechev // utils.js export const add = (a, b) => a + b; export const subtract = (a, b) => a - b; /******/ (() => { // webpackBootstrap /******/ "use strict"; // CONCATENATED MODULE: ./utils.js ** const add = (a, b) => a + b; const subtract = (a, b) => a - b; // CONCATENATED MODULE: ./index.js ** const index_subtract = (a, b) => a - b; console.log(add(1, 2)); /******/ })(); // index.js import { add } from './utils'; const subtract = (a, b) => a - b; console.log(add(1, 2)); webpack bundling

Slide 93

Slide 93 text

@mgechev // utils.js export const add = (a, b) => a + b; export const subtract = (a, b) => a - b; /******/ (() => { // webpackBootstrap /******/ "use strict"; // CONCATENATED MODULE: ./utils.js ** const add = (a, b) => a + b; const subtract = (a, b) => a - b; // CONCATENATED MODULE: ./index.js ** const index_subtract = (a, b) => a - b; console.log(add(1, 2)); /******/ })(); // index.js import { add } from './utils'; const subtract = (a, b) => a - b; console.log(add(1, 2)); webpack bundling

Slide 94

Slide 94 text

@mgechev ngc tsc opt wp terser tsc

Slide 95

Slide 95 text

@filipematossilv @mgechev Terser ● ES2015 version of UglifyJS ● DCE using static analysis ● Annotations for side-effect free function calls ● Also does name mangling, inlining, whitespace removal ● Does not understand module loading!

Slide 96

Slide 96 text

@mgechev Dead Code Elimination

Slide 97

Slide 97 text

@filipematossilv (() =>{"use strict";console.log(3)}) () input output /******/ (() => { // webpackBootstrap /******/ "use strict"; // CONCATENATED MODULE: ./utils.js ** const add = (a, b) => a + b; const subtract = (a, b) => a - b; // CONCATENATED MODULE: ./index.js ** const index_subtract = (a, b) => a - b; console.log(add(1, 2)); /******/ })();

Slide 98

Slide 98 text

@mgechev Purity

Slide 99

Slide 99 text

@filipematossilv input output const getData = (a) => localStorage.get(a); getData(2, 3); (o =>localStorage.get(2))() const getData = (a) => localStorage.get(a); /*@ __PURE __ */getData(2, 3); Empty

Slide 100

Slide 100 text

@mgechev ngc tsc opt wp terser tsc

Slide 101

Slide 101 text

@mgechev Ivy @Component({ selector: 'app', template: ' ...' }) class AppComponent { ... } app.component.js app.component.d.ts

Slide 102

Slide 102 text

@Component({ selector: 'cmp', template: ` ` }) export class Cmp {} ... Cmp.ɵcmp = defineComponent({ type: Cmp, selectors: [['cmp']], template: function(fs, ctx) { if (fs & RenderFlags.Create) ...; if (fs & RenderFlags.Update) ...; }, directives: [NgIf] }); ngc

Slide 103

Slide 103 text

@mgechev

Slide 104

Slide 104 text

@mgechev ngc tsc opt wp terser tsc

Slide 105

Slide 105 text

@filipematossilv @mgechev Build Optimizer ● Part of Angular Devkit ● Post processor for TS and Angular AOT code ● Enable Terser DCE through refactoring ● Adds side-effect free annotations

Slide 106

Slide 106 text

@filipematossilv class MyClass {}; MyClass.prop = 42; // terser output (class{}).prop=42; const MyClass = /*@ __PURE __ */(() => { class MyClass { } MyClass.prop = 42; return MyClass; })(); // terser output // nothing! input output

Slide 107

Slide 107 text

@mgechev ngc tsc opt wp terser tsc

Slide 108

Slide 108 text

twitter.com/mgechev

Slide 109

Slide 109 text

@yourtwitter @mgechev Differential loading ● Produce ES5 bundles for newer browsers ● Do not send polyfills to modern browsers ● Smaller payload ● Do not downlevel modern features ● Faster execution ● Smaller payload

Slide 110

Slide 110 text

@mgechev -65KB polyfills ~2-10% smaller bundles

Slide 111

Slide 111 text

@mgechev Step 1: Load HTML Step 2: Look at script tags Step 2: Download right version Differential loading

Slide 112

Slide 112 text

@yourtwitter Differential loading Differential loading

Slide 113

Slide 113 text

@yourtwitter Differential loading Differential loading

Slide 114

Slide 114 text

@filipematossilv @mgechev Putting it all together ● ngc ○ Compiles templates to efficient instructions ● Webpack ○ only includes used modules in side-effect free libraries ○ makes large modules from many small modules ● Build Optimizer ○ refactors TS and AOT code for DCE ○ adds side-effect free annotations for terser ● Terser ○ takes large annotated modules and removes all unused code ● TypeScript ○ downlevels ES2015 to ES5

Slide 115

Slide 115 text

@mgechev Debugging your build

Slide 116

Slide 116 text

@yourtwitter $ NG_BUILD_PROFILING=true ng build --prod Profile your build

Slide 117

Slide 117 text

@mgechev

Slide 118

Slide 118 text

@mgechev

Slide 119

Slide 119 text

@mgechev Developer facing APIs

Slide 120

Slide 120 text

@yourtwitter $ ng build --prod Building a project

Slide 121

Slide 121 text

@mgechev

Slide 122

Slide 122 text

@mgechev

Slide 123

Slide 123 text

@mgechev Thank you! twitter.com/mgechev github.com/mgechev blog.mgechev.com Survey: mgv.io/talk