As those who have been following my Angular 2 series know, I’ve been using Angular CLI for creating and organizing projects. Although it automatically provides one with a basic project template, sometimes ‘basic’ is not enough. A project may need to be modified for any number of reasons - a common case is when one needs to manually add certain libraries, even when using npm to install them.

To modify the internals of a project, it is necessary to first understand the AngularJS 2 workflow and directory structure. When a new project is created through build tools like CLI, gulp , npm , etc., control files are created along with the source code. Most Angular tutorials cover these files only as a corollary to explaining the core TypeScript language. For someone who just wants to change their project’s behavior a bit, those may be needlessly complex. So, in this article, I will explain the files controlling the AngularJS 2 workflow and how the workflow can be customized with them. Let’s get started!

Before starting, though, please be aware that this article draws on the previous articles it is assumed that you are familiar with how to build projects through the CLI , through using the starter/basic template without the CLI , and at least the basics of project structure (e.g. components, index.html, etc.) .

Understanding Project Workflow

Most online tutorials on the CLI go over the command-line options and give some functional overview, but do not go into detail on what project management files are created nor what purpose those files serve. In order to understand a project that is created through the CLI, let’s start by creating a new project:

$ng new AngularJS2-Workflow

This will automatically create several directories and files. Let’s focus on the ones most relevant to workflow customization.

First, the src/ directory is created, which contains all of the project’s source code. In that directory will be main.ts (among other files). That is entry point for the application - where the execution starts. In general, main.ts does not need to be manually modified, as

$ng generate <some-component>

will automatically update and add the new components as needed.

The e2e directory stores end-to-end test files. These are separate from unit tests. The AngularJS 2 CLI supports unit testing natively through Karma and end-to-end testing through Protractor, with configuration files karma.conf and protractor.conf , respectively. Please see here for more information on Protractor and here for more on Karma. While testing is important to a workflow, it is not the subject of this tutorial and may be revisited in a future article.

The node_modules directory is not automatically generated at project creation, but will be automatically created through installing modules via npm install . This directory will contain the libraries, along with their type definitions. (More on type definitions later on.) Although libraries can be installed globally, the general practice is to install libraries uniquely per-module. If migrating from earlier versions of AngularJS, it is important to note that modules are compiled via SystemJS and not Webpack. (For more on the differences, please see here.

Understanding Auto-generated Project Files

package.json

package.json is a control file for the npm package management system, one of the systems on which AngularJS 2 depends. It defines all of meta-information for the project and all of its dependencies. In many senses, it is the single most important file to the workflow.

The default package.json created by the CLI looks something like this:

package.json { "name": "angular-js2-workflow", "version": "0.0.0", "license": "MIT", "angular-cli": {}, "scripts": { "ng": "ng", "start": "ng serve", "lint": "tslint \"src/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check", "test": "ng test", "pree2e": "webdriver-manager update --standalone false --gecko false", "e2e": "protractor" }, "private": true, "dependencies": { "@angular/common": "^2.3.1", "@angular/compiler": "^2.3.1", "@angular/core": "^2.3.1", "@angular/forms": "^2.3.1", "@angular/http": "^2.3.1", "@angular/platform-browser": "^2.3.1", "@angular/platform-browser-dynamic": "^2.3.1", "@angular/router": "^3.3.1", "core-js": "^2.4.1", "rxjs": "^5.0.1", "ts-helpers": "^1.1.1", "zone.js": "^0.7.2" }, "devDependencies": { "@angular/compiler-cli": "^2.3.1", "@types/jasmine": "2.5.38", "@types/node": "^6.0.42", "angular-cli": "1.0.0-beta.26", "codelyzer": "~2.0.0-beta.1", "jasmine-core": "2.5.2", "jasmine-spec-reporter": "2.5.0", "karma": "1.2.0", "karma-chrome-launcher": "^2.0.0", "karma-cli": "^1.0.1", "karma-jasmine": "^1.0.2", "karma-remap-istanbul": "^0.2.1", "protractor": "~4.0.13", "ts-node": "1.2.1", "tslint": "^4.3.0", "typescript": "~2.0.3" } }

(Note: As the CLI is currently in beta, some of this may look slightly different. Please see here for the current version.)

Note the two large arrays - dependencies and devDependencies . What’s the difference?

Any library needed at runtime belongs in the dependencies array. When installing a library via npm install , the dependencies array will be automatically edited and updated. While anyone using the project will need all the dependencies (that’s why they’re called dependencies, after all), the end-users probably don’t need (or care about) the testing frameworks, documentation generators, code checkers - all of the build-time requirements. Those belong in the devDependencies array, and are not installed automatically with the generated project.

For more information about the format and capabilities of the package.json file, please see here.

angular-cli.json

angular-cli.json defines asset (resource) locations and directory structure, and looks something like this:

angular-cli.json { "project": { "version": "1.0.0-beta.26", "name": "angular-js2-workflow" }, "apps": [ { "root": "src", "outDir": "dist", "assets": [ "assets", "favicon.ico" ], "index": "index.html", "main": "main.ts", "polyfills": "polyfills.ts", "test": "test.ts", "tsconfig": "tsconfig.json", "prefix": "app", "styles": [ "styles.css" ], "scripts": [], "environments": { "source": "environments/environment.ts", "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts" } } ], "e2e": { "protractor": { "config": "./protractor.conf.js" } }, "test": { "karma": { "config": "./karma.conf.js" } }, "defaults": { "styleExt": "css", "prefixInterfaces": false, "inline": { "style": false, "template": false }, "spec": { "class": false, "component": true, "directive": true, "module": false, "pipe": true, "service": true } } }

To share resources between packages to use assets outside of the project tree, simply modify the appropriate entries in this file.

index.html

When initially installed, this does nothing, as can be seen below:

<!doctype html> <html> <head> <meta charset="utf-8"> <title>AngularJS 2 Workflow</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <angular-js2-workflow-root>Loading...</angular-js2-workflow-root> </body> </html>

However, the CLI will automatically add script lines to load libraries as part of the install process, and the main.ts , after transpilation, will be added as main.js .

tsconfig.json

This contains the TypeScript configuration:

{ "compilerOptions": { "baseUrl": "", "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, "lib": ["es6", "dom"], "mapRoot": "./", "module": "es6", "moduleResolution": "node", "outDir": "src/dist/out-tsc", "sourceMap": true, "target": "es5", "typeRoots": [ "src/node_modules/@types" ] } }

As mentioned in the introduction, this tutorial will not go into TypeScript. For more information on TypeScript and the tsconfig.json file, please see here.

typings.json

The typings.json file does not exist by default, but is created and updated as libraries are installed. JavaScript does not have (exactly) have namespaces, so Typings (the TypeScript Definition Manager) is a library to wrap modules in their own “namespace” to avoid naming conflicts whilst still providing access to the functionality.

AngularJS 2 modules provide their typing information in .d.ts files, which are automatically added to the typings directory at the time of install. For external libraries, the typing information must be installed manually.

Customizing The Workflow

While we have used the files created by the CLI as our examples thus far, to explain customization we will use the basic template as used in previous articles.

Installing Libraries

From the basic template, package.json looks a bit different:

package.json { "name": "angular-2", "version": "1.0.0", "scripts": { "start": "concurrently \"npm run tsc:w\" \"npm run lite\" ", "tsc": "tsc", "tsc:w": "tsc -w", "lite": "lite-server", "typings": "typings", "postinstall": "typings install" }, "license": "ISC", "dependencies": { "angular2": "2.0.0-beta.13", "systemjs": "0.19.25", "es6-shim": "^0.35.0", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.2", "zone.js": "0.6.6" }, "devDependencies": { "concurrently": "^2.0.0", "lite-server": "^2.1.0", "typescript": "^1.8.9", "typings":"^0.7.11" } }

Here, the dependencies are (mostly) not defined in terms of modules, but rather in terms of packages. Also, as this is a much simpler template than what the CLI provides, very few libraries are initially included. Let’s fix that in a simple case:

$npm install –save-dev semver

That updates the package.json file by adding those libraries to the devDependencies array.

There will also be a typings.json file in the node_modules directory:

typings.json { "ambientDependencies": { "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd", "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd" } }

Now, to install the typings information, do:

$typings install npm:semver

And then we run:

$npm install

index.html is also different:

index.html <!DOCTYPE html> <html lang="en"> <head> <title>Angular 2 QuickStart</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <!-- Load libraries (Note: IE required polyfills, in this exact order_ --> <script src="node_modules/es6-shim/es6-shim.min.js"></script> <script src="node_modules/systemjs/dist/system-polyfills.js"></script> <script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script> <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="node_modules/rxjs/bundles/Rx.js"></script> <script src="node_modules/angular2/bundles/angular2.dev.js"></script> <!-- Configure SystemJS --> <script> System.config({ packages: { app: { format: 'register', defaultExtension: 'js' } } }); System.import('app/js/main').then(null, console.error.bind(console)); </script> </head> <body> <div class="container-fluid"> <my-app>Loading...</my-app> </div> </body> </html>

This shows a little better how libraries can be manually preloaded.

All that’s left is to modify app/ts/app.component.ts with your code, and:

$npm start

should show the running server, with several files in app/js transpiled and loaded.