File: README.md
Angular CLI - The CLI tool for Angular.
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold,
and maintain Angular applications directly from a command shell.
Contributing Guidelines
ยท
Submit an Issue
ยท
Blog
Documentation
Get started with Angular CLI, learn the fundamentals and explore advanced topics on our documentation website.
Development Setup
Prerequisites
- Install Node.js which includes Node Package Manager
Setting Up a Project
Install the Angular CLI globally:
npm install @angular/cliCreate workspace:
ng new [PROJECT NAME]Run the application:
cd [PROJECT NAME]
ng serveAngular is cross-platform, fast, scalable, has incredible tooling, and is loved by millions.
Quickstart
Ecosystem
Changelog
Learn about the latest improvements.
Upgrading
Check out our upgrade guide to find out the best way to upgrade your project.
Contributing
Contributing Guidelines
Read through our contributing guidelines to learn about our submission process, coding rules and more.
Want to Help?
Want to report a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for contributing and then check out one of our issues labeled as help wanted or good first issue.
Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our Code of Conduct.
Developer Guide
Read through our developer guide to learn about how to build and test the Angular CLI locally.
Community
Join the conversation and help the community.
- X (formerly Twitter)
- Discord
- Gitter
- YouTube
- StackOverflow
- Find a Local Meetup
Packages
This is a monorepo which contains many tools and packages:
Tools
| Project | Package | Version | Links |
|---|---|---|---|
| Angular Build System | @angular/build |
||
| Angular CLI | @angular/cli |
||
| Schematics CLI | @angular-devkit/schematics-cli |
Packages
| Project | Package | Version | Links |
|---|---|---|---|
| Angular SSR | @angular/ssr |
||
| Architect | @angular-devkit/architect |
||
| Build Angular | @angular-devkit/build-angular |
||
| Build Webpack | @angular-devkit/build-webpack |
||
| Core | @angular-devkit/core |
||
| Schematics | @angular-devkit/schematics |
Misc
| Project | Package | Version | Links |
|---|---|---|---|
| Angular Create | @angular/create |
||
| Webpack Angular Plugin | @ngtools/webpack |
Schematics
| Project | Package | Version | Links |
|---|---|---|---|
| Angular PWA Schematics | @angular/pwa |
||
| Angular Schematics | @schematics/angular |
Love Angular CLI? Give our repo a star :star: :arrow_up:.
File: docs/design/analytics.md
Usage Metrics Gathering
This document list exactly what is gathered and how.
Any change to analytics should most probably include a change to this document.
Dimensions and Metrics
Google Analytics Custom Dimensions are used to track system values and flag values. These
dimensions are aggregated automatically on the backend.
One dimension per flag, and although technically there can be an overlap between 2 commands, for
simplicity it should remain unique across all CLI commands. The dimension is the value of thex-user-analytics field in the schema.json files.
Adding dimension or metic.
- Create the dimension or metric in Google Analytics first. These are not tracked if they aren't
defined in Google Analytics. - Use the ID of the dimension as the
x-user-analyticsvalue in theschema.jsonfile. - New dimension and metrics PRs need to be approved by the tooling lead and require a new Launch.
Deleting a dimension or metic.
- Archive the dimension and metric in Google Analytics.
DO NOT ADD x-user-analytics FOR VALUES THAT ARE USER IDENTIFIABLE (PII), FOR EXAMPLE A
PROJECT NAME TO BUILD OR A MODULE NAME.
Limits
| Item | Standard property limits |
|-------------------------------- |-------------------------- |
| Event-scoped custom dimensions | 50 |
| User-scoped custom dimensions | 25 |
| All custom metrics | 50 |
List of User Custom Dimensions
| Name | Parameter | Type |
|---|---|---|
| UserId | up.ng_user_id |
string |
| OsArchitecture | up.ng_os_architecture |
string |
| NodeVersion | up.ng_node_version |
string |
| NodeMajorVersion | upn.ng_node_major_version |
number |
| AngularCLIVersion | up.ng_cli_version |
string |
| AngularCLIMajorVersion | upn.ng_cli_major_version |
number |
| PackageManager | up.ng_package_manager |
string |
| PackageManagerVersion | up.ng_pkg_manager_version |
string |
| PackageManagerMajorVersion | upn.ng_pkg_manager_major_v |
number |
List of Event Custom Dimensions
| Name | Parameter | Type |
|---|---|---|
| Command | ep.ng_command |
string |
| SchematicCollectionName | ep.ng_schematic_collection_name |
string |
| SchematicName | ep.ng_schematic_name |
string |
| Standalone | ep.ng_standalone |
string |
| SSR | ep.ng_ssr |
string |
| Style | ep.ng_style |
string |
| Routing | ep.ng_routing |
string |
| InlineTemplate | ep.ng_inline_template |
string |
| InlineStyle | ep.ng_inline_style |
string |
| BuilderTarget | ep.ng_builder_target |
string |
| Aot | ep.ng_aot |
string |
| Optimization | ep.ng_optimization |
string |
List of Event Custom Metrics
| Name | Parameter | Type |
|---|---|---|
| AllChunksCount | epn.ng_all_chunks_count |
number |
| LazyChunksCount | epn.ng_lazy_chunks_count |
number |
| InitialChunksCount | epn.ng_initial_chunks_count |
number |
| ChangedChunksCount | epn.ng_changed_chunks_count |
number |
| DurationInMs | epn.ng_duration_ms |
number |
| CssSizeInBytes | epn.ng_css_size_bytes |
number |
| JsSizeInBytes | epn.ng_js_size_bytes |
number |
| NgComponentCount | epn.ng_component_count |
number |
| AllProjectsCount | epn.all_projects_count |
number |
| LibraryProjectsCount | epn.libs_projects_count |
number |
| ApplicationProjectsCount | epn.apps_projects_count |
number |
Debugging
Using NG_DEBUG=1 will enable Google Analytics debug mode, To view the debug events, in Google Analytics go to Configure > DebugView.
Disabling Usage Analytics
There are 2 ways of disabling usage analytics:
- using
ng analytics disable --global(or changing the global configuration file yourself). This is the same
as answering "No" to the prompt. - There is an
NG_CLI_ANALYTICSenvironment variable that overrides the global configuration.
That flag is a string that represents the User ID. If the string"false"is used it will
disable analytics for this run.
File: docs/design/build-system.md
Build System
Angular CLI includes a first-party build system for Angular applications distributed as @angular-devkit/build-angular.
This build system is responsible for creating a standalone single-page application (SPA) from user source files and third-party dependencies.
@angular-devkit/build-angular itself integrates with the rest of Angular CLI by being an Architect builder.
This document describes a top level view of the functionality in @angular-devkit/build-angular, referred as just the "build system".
Deprecated or soon to be removed features are not described here.
In broad strokes the main areas are:
- loading and processing sources
- code splitting
- production optimizations
- post-processing steps
Many tools are used in this process, and most of these steps happen within a Webpack build.
We maintain a number of Webpack-centric plugins in this repository, some of these are public but most are private since they are very specific to our setup.
Overview diagram
Below is a diagram of processing sources go through.
It's not strictly accurate because some options remove certain processing stages while adding others.
This diagram doesn't show these conditionals and only shows the possible processing steps.
Relative paths, such as ./raw-css-loader.ts, refer to internal plugins, while other names usually refer to public npm packages.
Loading and processing sources
Sources for Angular CLI browser apps are comprised of TypeScript files, style sheets, assets, scripts, and third party dependencies.
A given build will load these sources from disk, process them, and bundle them together.
TypeScript and Ahead-Of-Time Compilation
Angular builds rely heavily on TypeScript-specific functionality for Ahead-of-Time template compilation (AOT).
Outside Angular CLI, this is performed by the Angular Compiler (ngc), provided by @angular/compiler-cli.
To avail of Ahead-of-Time template compilation within a Webpack compilation we use and distribute the @ngtools/webpack Webpack plugin.
Typescript sources are loaded from disk and compiled in-memory into JavaScript files that are stored in a virtual file system and made available to Webpack.
During compilation we also perform a number of code transformations using TypeScript transformers that enable automatic usage of AOT, internationalization features, and server-side rendering.
AOT compilation requires loading HTML and CSS resources, referenced on Angular Components, as standalone strings with no external dependencies.
However, Webpack compilations operate on the basis of modules and references between them.
It's not possible to get the full compilation result for a given resource before the end of the compilation in webpack.
To obtain the standalone string for a HTML/CSS resource we compile it using a separate Webpack child compilation then extract the results.
These child compilations inherit configuration and access to the same files as the parent compilation, but have their own compilation life cycle and complete independently.
The build system allows specifying replacements for specific files by replacing what path is loaded from the virtual file system.
This is used for conditional loading of code at build time.
Stylesheets
Two types of stylesheets are used in the build system: global stylesheets and component stylesheets.
Global stylesheets are injected into the index.html file, while component stylesheets are loaded directly into compiled Angular components.
The build system supports plain CSS stylesheets as well as the Sass and LESS CSS pre-processors.
Stylesheet processing functionality is provided by sass-loader, less-loader, postcss-loader, postcss-import, augmented in the build system by custom webpack plugins.
Assets
Assets in the build system refer specifically to a list of files or directories that are meant to be copied verbatim as build artifacts.
These files are not processed and commonly include images, favicons, pdfs and other generic file types.
They are loaded into the compilation using copy-webpack-plugin.
Scripts
Scripts in the build system refer specifically to JavaScript files that are meant to be loaded directly on index.html without being processed.
They are loaded into the compilation using a custom webpack plugin.
Third party dependencies
Third party dependencies are mostly inside node_modules and are referenced via imports in source files.
Stylesheet third party dependencies are treated mostly the same as sources.
JavaScript third party dependencies suffer a more involved process.
They are first resolved to a folder in node_modules via Node Module Resolution.
A given module might have several different entry points, for instance one for use in NodeJS and another one for using in the browser.
Although package.json only officially supports listing one entry point under the main key, it's common for npm packages to list other entry points.
Each entry point is listed in under a key in that module's package.json whose value is a string containing a relative file path.
We use es2015 > browser > module > main a priority list of keys, where the first key matched name determines which entry point to use.
For instance, for a module that has both browser and main entry points, we pick browser.
Once the actual JavaScript file is determined, it is loaded into the compilation together with it's source map.
This resolution strategy supports the Angular Package Format.
Code splitting
Code is automatically split into different files (or chunks, for js files) based on a few different triggers.
The main TypeScript entry point and it's dependencies are bundled into the main chunk.
Global styles and scripts get one file per entry, named after themselves.
JavaScript code imported only via dynamic imports is automatically split into a separate chunk that is loaded asynchronously named after the file containing the dynamic import.
If multiple asynchronous chunks contain a reference to the same module, it is placed in a new asynchronously loaded chunk named after the other chunks that use it.
There is also a special chunk called runtime that contains the module loading logic and is loaded before the others.
Optimizations
The build system contains optimizations aimed at improving the performance (for development builds) or the size of artifacts (for production builds).
These are often mutually exclusive and thus we cannot just default to always using them.
Development optimizations
Development optimizations focus on reducing rebuild time on watched builds.
Although faster is always better, our threshold is to keep rebuilds even for large projects below 2 seconds.
Computation needed to bundle code grows with its total size because of the cost of string concatenation and source map operations.
Third party dependencies that are initially loaded are split into a synchronously loaded chunk called vendor.
Splitting the infrequently changed vendor code from the frequently changed source code thus helps make rebuilds faster.
When processing stylesheets, Webpack stores the intermediate modules as JavaScript code.
The JavaScript wrapper code makes stylesheets larger and mini-css-extract-plugin must be used to obtain the actually stylesheet content into a CSS file.
In development however, we skip the CSS extraction and leave it as JavaScript code for faster rebuild times.
Watched builds split the processing load of TypeScript compilation between file emission on the main process and type checking on a forked process.
Large projects can also opt-out of AOT compilation for faster rebuilds.
Production optimizations
Angular CLI focuses on enabling tree-shaking (removing unused modules) and dead code elimination (removing unused module code).
These two categories have high potential for size reduction because of network effects: removing code can lead to more code being removed.
The main tool we use to achieve this goal are the dead code elimination capabilities of Terser.
We also use Terser's mangling, by which names, but not properties, are renamed to shorter forms.
The main characteristics of Terser to keep in mind is that it operates via static analysis and does not support the indirection introduced by module loading.
Thus the rest of the pipeline is directed towards providing Terser with code that can be removed via static analysis in large single modules scopes.
To this end we developed @angular-devkit/build-optimizer, a post-processing tool for TS code.
Build Optimizer searches for code patterns produced by the TypeScript and Angular compiler that are known to inhibit dead code elimination, and converts them into equivalent structures that enable it instead (the link above contains some examples).
It also adds Terser annotations marking top-level functions as free from side effects for libraries that have the sideEffects flag set to false in package.json.
Webpack itself also contains two major features that enable tree-shaking and dead code elimination: sideEffects flag support and module concatenation.
Having the sideEffects flag set to false in package.json of a library means that library has no top-level side-effects and only exposes imports, which allows Webpack to rewrite imports to that library directly to the modules used and not including non-imported modules at all.
Module concatenation allows Webpack to collect in a single module the content of several modules, which in turn allows Terser to more easily remove unused code since there is no module loading indirection between those modules.
One significant pitfall of this optimization strategy is the use of code splitting.
Using code splitting is desirable in order to speed up loading of web apps by deferring code that is not necessary on the initial load.
But since code splitting necessarily makes use of module loading, it is at odds with Terser-based optimizations.
The use of lazy loading can not only prevent further optimizations, but also regress the currently possible ones by preventing module concatenation.
Modules that were concatenated when lazy modules are not present might not be concatenated anymore after lazy loading is introduced because these modules now need to be accessed from the lazy modules and thus get their own module scope.
Aside from tree-shaking, scripts and styles (as defined in the sources above) also undergo optimizations via Terser and CleanCSS respectively.
Post-processing steps
There are some steps that are meant to operate over whole applications and thus happen after the compilation finishes and outputs files.
The steps are described in the order in which they are executed during a build.
The execution order was determined based on the complexity of the step with a primary goal of minimizing the repetition of computationally expensive operations.
Differential Loading
Differential loading is a strategy that allows your web application to support multiple browsers, but only load the necessary code that the browser needs.
When differential loading is enabled, the CLI generates two distinct variants of application bundles.
- The first contains ES2015 syntax, takes advantage of built-in support in modern browsers, ships fewer polyfills, and results in a smaller total size.
- The second contains code in the older ES5 syntax, along with all necessary polyfills for Angular to function. This results in a larger total size, but supports older browsers.
This process as designed has the advantage that only one full compilation of the application in ES2015 syntax is required.
This removes a large amount of otherwise unnecessary and duplicate processing such as module resolution and dead code elimination.
It also guarantees that the application is ES5 compliant including third-party code.
The two variants of application bundles are created by the following steps:
- A full build of the application is performed using an ES2015 output target.
The application's global stylesheets, scripts, and assets are also processed during this step via the full build. These elements are reused for both of application variants. - A copy of the JavaScript output application files are transformed (commonly referred to as down-leveled) to ES5 compatible syntax.
ES2015+ syntax elements such as classes are converted into functionally equivalent ES5 code structures. - An additional ES5-only polyfills file is generated that contains the required Angular polyfills for ES5-only browsers.
- A single index HTML file is created that references both application variants and is designed to only load the appropriate files for each browser.
To support loading the file sets in the appropriate browsers, the HTML script element's type and nomodule attributes are leveraged.
Browsers will only load a script with a known type.
The ES2015 files are referenced using a type of module which is only supported on browsers that support ES2015+ code.
Since browsers that do not support ES2015+ code also do not support the module script type, these scripts are ignored for browsers that cannot parse and execute the ES2015 code.
Browsers that support the module script type also support the nomodule attribute.
This attribute instructs a browser that supports module scripts to ignore the script with the attribute. There is one browser exception in this case: Safari 10.1.
This browser supports module scripts but does not support the nomodule attribute.
To support this case, a special polyfill script is included to provide a workaround for the browser.
This arrangement of script elements ensures that ES5-only browsers will only execute the ES5 script files and browsers that support ES2015+ will only execute the ES2015 script files.
Localization (i18n)
The second of these post-processing steps is build-time localization.
The final js bundles are processed using @angular/localize, replacing any locale-specific translations.
This sort of localization produces one application for each locale, each in their own folders.
Service Worker
The third and last post-processing step is the creation of a service worker.
A listing of final application files is taken, fingerprinted according to their content, and added to the service worker manifest.
This must be the last step because it needs each application file to not be modified further.
File: docs/design/deployurl-basehref.md
| Deploy URL | Base HREF | |
|---|---|---|
| Initial scripts (index.html) | โ ๐ | โ ๐ |
| Initial stylesheets (index.html) | โ ๐ | โ ๐ |
| Lazy scripts (routes/import()/etc.) | โ ๐ | โ ๐ |
| Processed CSS resources (images/fonts/etc.) | โ ๐ | โ ๐ |
| Relative template (HTML) assets | โ ๐ | โ ๐ |
| Angular Router Default Base (APP_BASE_HREF) | โ | โ *1 |
| Single reference in deployed Application | โ ๐ | โ ๐ |
| Special resource logic within CLI | โ ๐ | โ ๐ |
| Relative fetch/XMLHttpRequest | โ | โ |
โ
- has/affects the item/trait
โ - does not have/affect the item/trait
๐ - favorable behavior
๐ - unfavorable behavior
*1 -- Users with more complicated setups may need to manually configure the APP_BASE_HREF token within the application. (e.g., application routing base is / but assets/scripts/etc. are at /assets/)
File: docs/design/ngConfig.md
ngConfig - Design
Goals
Currently, a project scaffolded with the CLI has no way of specifying options and configurations affecting their projects. There are ways to affect the build (with the angular-cli-build.js file), but the following questions cannot be answered without actual project options:
- Where in my directory is my karma.conf file?
- What is my firebase database URL?
- Where is my client code?
- How can I use a different lazy-loading boundary prefix (or none at all)?
- Any other backend I want to run prior to
ng serve?
Proposed Solution
Since the data is static, we only need to keep it in a static store somewhere.
One solution would be to keep the data in the package.json. Unfortunately, the metadata contains too much data and the package.json file would become unmanageable.
Instead of polluting the package file, a .angular-cli.json file will be created that contains all the values. Access to that file will be allowed to the user if he knows the structure of the file (unknown keys will be kept but ignored), and it's easy to read and write.
Fallback
There should be two .angular-cli.json files; one for the project and a general one. The general one should contain information that can be useful when scaffolding new apps, or information about the user.
The project .angular-cli.json goes into the project root. The global configuration should live at $HOME/.angular-cli.json.
Structure
The structure should be defined by a JSON schema (see here). The schema will be used to generate the d.ts, but that file will be kept in the file system along with the schema for IDEs.
Every PR that would change the schema should include the update to the d.ts.
API
CLI
Getting values
The new command get should be used to output values on the terminal. It takes a set of flags and an optional array of paths;
--globor-g; the path follows a glob format, where*can be replaced by any amount of characters and?by a single character. This will outputname=valuefor each values matched.
Otherwise, outputs the value of the path passed in. If multiple paths are passed in, they follow the format of name=value.
Setting values
The new command set should be used to set values in the local configuration file. It takes a set of flags and an optional array of [path](#path)=value;
--global; sets the value in the global configuration.--remove; removes the key (no value should be passed in).
The schema needs to be taken into account when setting the value of the field;
- If the field is a number, the string received from the command line is parsed.
NaNthrows an error. - If the field is an object, an error is thrown.
- If the path is inside an object but the object hasn't been defined yet, sets the object with empty values (use the schema to create a valid object).
Path
The paths are json formatted path; each . separates a map, while [] indicates an index in an array.
An example is the following:
keyA.keyB.arrayC[3].valueModel
A model should be created that will include loading and saving the configuration, including the global configuration.
The model should be part of the project and created on the project object.
That model can be used internally by the tool to get information. It will include a proxy handler that throws if an operation doesn't respect the schema. It will also set values on globals and locals depending on which branches you access.
A simple API would return the TypeScript interface:
class Config {
// ...
get local(): ICliConfig {
/* ... */
}
get global(): ICliConfig {
/* ... */
}
}The local and global getters return proxies that respect the JSON Schema defined for the Angular config. These proxies allow users to not worry about the existence of values; those values will only be created on disc when they are set.
Also, local will always defer to the same key-path in global if a value isn't available. If a value is set and the parent exists in global, it should be created to local such that it's saved locally to the project. The proxies only care about the end points of local and global, not the existence of a parent in either.
For example, assuming the following globals/locals:
// Global
{
"key1": {
"key2": {
"value": 0,
"value2": 1
}
}
}
// Local
{
"key1": {
"key2": {
"value2": 2,
"value3": 3
}
}
}The following stands true:
const config = new Config(/* ... */);
console.log(config.local.key1.key2.value); // 0, even if it doesn't exist.
console.log(config.local.key1.key2.value2); // 2, local overrides.
console.log(config.local.key1.key2.value3); // 3.
console.log(config.local.key1.key2.value4); // Schema's default value.
console.log(config.global.key1.key2.value); // 0.
console.log(config.global.key1.key2.value2); // 1, only global.
console.log(config.global.key1.key2.value3); // Schema's default value.
config.local.key1.key2.value = 1;
// Now the value is 1 inside the local. Global stays the same.
config.local.key1.key2.value3 = 5;
// The global config isn't modified.
config.global.key1.key2.value4 = 99;
// The local config stays the same.
console.log(config.local.key1.key2.value4); // 99, the global value.
config.save(); // Commits if there's a change to global and/or local.File: docs/design/third-party-libraries.md
Installing third party libraries
Abstract
The current ng install process is faulty; third parties have to add a bundles/ directory
with metadata that we expect. This document explores ways to improve on the process.
Requirements
The following use cases need to be supported by ng install:
- Support for any thirdparties, ultimately falling back to running
npm installonly and
doing nothing else, if necessary. - Not a new package manager.
- Metadata storage by thirdparties in
package.json. This must be accessible by plugins for
the build process or even when scaffolding. - Proper configuration of SystemJs in the browser.
- SystemJS configuration managed by the user needs to be kept separated from the autogenerated
part. - The build process right now is faulty because the
tmp/directory used by Broccoli is
inside the project. TypeScript when usingmoduleResolution: "node"can resolve thenode_modulesdirectory (since it's in an ancestor folder), which means that TypeScript
compiles properly, but the build does not copy files used by TypeScript todist/. We need
to proper map the imports and move them totmpbefore compiling, then todist/properly. - Potentially hook into scaffolding.
- Potentially hook into the build.
- Safe uninstall path.
Usages
Here's a few stories that should work right off:
$ ng new my-app && cd my-app/
$ ng install jquery^(this makes jQuery available in the index)
$ ng install angularfire2
> Please specify your Firebase database URL [my-app.firebaseio.com]: _^(makes firebase available and provided to the App)
$ ng install angularfire2 --dbUrl=my-firebase-db.example.com^(skip prompts for values passed on the command line)
$ ng install angularfire2 --quiet^(using --quiet to skip all prompts and use default values)
$ ng install less^(now compiles CSS files with less for the appropriate extensions)
Proposed Solution
The install task will perform the following subtasks:
Run
npm install ${libName}. On failure, fail the install process.Run
preinstallscripts. If any script fails, runnpm uninstall ${libName}and fail
the install process.Copy the
appDatato theangular.jsonfile of the generated app, creating it if it
doesn't exist, under the"modules"key. This data will be sent to the App as is. See the
appData section for more information.Read the
package["angular-cli"].appPromptand prompt the user configuration values,
if any. See the appData section for more information.Add the providers specified in
package["angular-cli"]["providers"]to the angular.jsproviderslist. Rebuild theproviders.jsfile. See the Providers
section for more information.Run
package["angular-cli"].scripts["install"]if it exists. If the script fails,
runnpm uninstall ${libName}and fail the install process.Detect if a package named
angular/cli-wrapper-${libName}exist in the angular
organization. If so, run the steps above as if ng install angular/angular-${libName}. If
this install fails, ignore the failure.These packages can be used to wrap libraries that we want to support but can't update
easily, like Jasmine or LESS.Install typings. See the Typings section.
Run
postinstallscripts.
Proof of Concept
A proof of concept is being developed.
Hooks
The third party library can implement hooks into the scaffolding, and the build system. The
CLI's tasks will look for the proper hooks prior to running and execute them.
The order of execution of these hooks is breadth first, going through all node packages and
checking for the package['angular-cli']['hooks']['${hookName}']. The hooks are thenrequire()'d as is, from within the app root folder. Within the same level of the dependency
tree, there is no guarantee for the order of execution.
Install Hooks
The only tricky part here is install hooks. The installed package should recursively call
its (pre|post|)install hooks only on packages that are newly installed. It should call(pre|post|)reinstall on those who were already installed. A map of the currently installed
packages should be kept before performing npm install.
appData
The angular-cli key in the generated app should be used for Angular CLI specific data.
This includes the CLI configuration itself, as well as third-parties library configuration.
Third-parties can store data that will be passed to the app, and can use that data themselves.
That data can be anything. Any keys starting with either $ or _ will be ignored and not
passed to the client. It could be used for builds or scaffolding information.
During installation, there's a step to prompt the user for data. The schema contains a prompt
text and a default value. The default value type is used to convert the string entered by the
user. The key used in appPrompt is the key saved in appData.
Example:
{ // ...
"angular-cli": {
"appPrompt": {
"number": {
"prompt": "Please enter a number:",
"defaultValue": 0
},
"url": {
"prompt": "URL of your website:",
"defaultValue": "${homepage}"
}
}
}
}The default value is necessary as a quiet mode is enforced, using default values to fill
in the data without user interaction. The special strings ${...} can be used to replace
with special values in the default string values. The values are taken from the package.json.
We use a declarative style to enforce sensible defaults and make sure developers think about
this. In the case where developers want more complex interaction, they can use the
install/uninstall hooks to prompt users. But 3rd party libraries developers need to be aware
that those hooks might not prompt users (no STDIN) and throw an error.
Providers
Adding Angular providers to the app should be seamless. The install process will create aproviders.js from all the providers contained in all the dependencies. The User can disclude
providers it doesn't want.
The providers.js file will always be overwritten by the install / uninstall process. It
needs to exist for toolings to be able to understand dependencies. These providers are global
to the application.
In order to prevent providers from being global, the user can use the --no-global-providers
flag during installation, or can change the dependencies by using ng providers. As an example:
ng new my-todo-app
ng generate component database
ng install --no-global-providers angularfirebase2
ng providers database angularfirebase2Or, alternatively, the user can add its own providers and dependencies to its components.
Dependencies
Because dependencies are handled by npm, we don't have to handle it.
Typings
Typings should be added, but failure to find typings should not be a failure of installation. The
user might still want to install custom typings himself in the worst case.
The typings package can be used to install/verify that typings exist. If the typings do not exist natively, we should tell the user to install the ambient version if he wants to.
Index.html
We do not touch the index.html file during the installation task. The default page should
link to a SystemJS configuration file that is auto generated by the CLI and not user
configurable (see SystemJS below). If the user install a third party library, like jQuery, and
wants to use it, they have to import it using import * as $ from 'vendor/jquery'.
The index.html also includes a section to configure SystemJS that can be managed by the user.
This is separate from the generated code.
SystemJS
It is important that SystemJS works without any modifications by the user. It is also important
to leave the liberty to the user to change the SystemJS configuration so that it fits their needs.
We will not use SystemJS bundles in development. This is for better debugging, future proofing
(when moving the build system) and better CDN support, as many of the loaded files will end up
being pulled from a CDN in production. During the ng build process for production, the
SystemJS configuration script will be rebuilt to fetch from the CDN.
Upgrade Strategy
The upgrade process simply uses NPM. If new appData is added, it should be added manually using
a migration hook for postinstall.
Remaining Problems
- Installing dependencies of packages need to be further sketched out.
- Need to add a fully fledged example with Firebase.
File: docs/process/bazel.md
Yarn Workspaces
The package architecture of angular-cli repository is originally setup using
yarn workspaces. This means the
dependencies of various package.json in the repository are linked and
installed together.
Bazel
Since then, Bazel was introduced to manage some of the build dependencies inangular-cli repo. However, Bazel does not yet support yarn workspaces,
since it requires laying out more than one node_modules directory. In this
mixed mode, developers ought to take extra care to synchronize the dependencies.
Since the yarn_install rule that installs all NPM dependencies in this
repository only reads from the root package.json, every Bazel target that
depends on packages downloaded from the NPM registry will need to have the
dependency declared in the root package.json.
In addition, if the dependency is also needed at runtime (non-dev dependencies),
the dependency on the individual package's package.json has to be updated as
well. This is to ensure that when users download a published version from NPM,
they will be able to install all dependencies correctly without Bazel. It is the
responsibility of the developer to keep both package.json in sync.
Windows support
In general, any sort of node file lookup on Bazel should be subject to require.resolve.
This is how rules_nodejs resolves paths using the Bazel runfiles mechanism, where a given
Bazel target only has access to outputs from its dependencies.
In practice, this does not make a lot of difference on Linux.
A symlink forest is laid down where the target is going to actually run, and mostly the
files are resolved correctly whether you use require.resolve or not because the files are there.
On Windows though, that's a stricter. Bazel does not lay down a symlink forest on
windows by default. If you don't use require.resolve, it's still possible to correctly
resolve some files, like outputs from other rules. But other files, like node modules
dependencies and data files, need to be looked up in the runfiles.
Since the requirement is quite lax on Linux but quite strict on windows, what ends up
happening is that lack of require.resolve calls go unnoticed until someone tries to run
things on Windows, at which point it breaks.
Debugging jasmine_node_test
On Linux, Bazel tests will run under a sandbox for isolation.
You can turn off this sandbox by adding the local = True attribute to the rule.
You can also force local execution by passing --test_output=streamed.
Then you will find the intermediate test files in bazel-out/k8-fastbuild/bin, followed by the test target path.
Tests that are sharded, via the shard_count attribute, can fail if you reduce the number of tests or focus only a few.
This will cause some shards to execute 0 tests, which makes the exit with an error code.
Tests that are marked as flaky, via the flaky attribute, will repeat when they fail.
This will cause any focused test to be repeated multiple time, since the presence of focused tests
causes jasmine to exit with a non-zero exit code.
While testing, you can remove the shard_count attribute to prevent sharding and the flaky
attribute to prevent repetition.
Setting --test_output=streamed will disable sharding and --flaky_test_attempts=1 will disable
the reruns of tests that have been marked as flaky.
The .bazelrc includes a config for running tests with remote debugging enabled:
pnpm bazel test --config=debug //packages/angular/cli:angular-cli_test
# Also disable reruns of failing tests that were marked as flaky:
pnpm bazel test --config=debug --config=no-sharding //packages/angular/cli:angular-cli_testFile: docs/process/release.md
Setting Up Local Repository
- Clone the Angular-CLI repo. A local copy works just fine.
- Create an upstream remote:
$ git remote add upstream https://github.com/angular/angular-cli.gitCaretaker
The caretaker should triage issues, merge PR, and sheppard the release.
Caretaker rotation can be found
here and individual shifts can
be modified as necessary to accommodate caretaker's schedules. This automatically syncs to a
Google Calendar
here.
Click the "+" button in the bottom right to add it to your calendar to see shifts alongside the
rest of your schedule.
The primary caretaker is responsible for both merging PRs and performing the weekly release.
The secondary caretaker does not have any direct responsibilities, but they may need to take
over the primary's responsibilities if the primary is unavailable for an extended time (a day
or more) or in the event of an emergency.
At the end of each caretaker's rotation, the primary should perform a handoff in which they
provide information to the next caretaker about the current state of the repository and update
the access group to now include the next caretakers. To perform this update to the access group,
the caretaker can run:
$ pnpm ng-dev caretaker handoffMerging PRs
The list of PRs which are currently ready to merge (approved with passing status checks) can
be found with this search.
This list should be checked daily and any ready PRs should be merged. For each PR, check thetarget label to understand where it should be merged to. You can find which branches a specific
PR will be merged into with the pnpm ng-dev pr check-target-branches command.
When ready to merge a PR, run the following command:
pnpm ng-dev pr mergeMaintaining LTS branches
Releases that are under Long Term Support (LTS) are listed on angular.dev.
Since there could be more than one LTS branch at any one time, PR authors who want to
merge commits into LTS branches must open a pull request against the specific base branch they'd like to target.
In general, cherry picks for LTS should only be done if it meets one of the criteria below:
- It addresses a critical security vulnerability.
- It fixes a breaking change in the external environment.
For example, this could happen if one of the dependencies is deleted from NPM. - It fixes a legitimate failure on CI for a particular LTS branch.
Release
Releasing is performed using Angular's unified release tooling. Each week, two releases are expected, latest and next on npm.
DURING a minor OR major CLI release:
Once FW releases the actual minor/major release (for example: 13.0.0 or 13.1.0), update dependencies with the following:
- Update
constants.bzlso@angular/coreandng-packagrare using the release version (drop-rc.*). - Update all
package.jsondependencies on framework packages to to the release version (drop-rc.*).- Components packages release after CLI, so those should be left as
-rc.*.
- Components packages release after CLI, so those should be left as
pnpm installto update thepnpm-lock.yamlfile.
Create a PR with the above changes (example) and merge it directly to the RC
branch. This PR must land after FW releases (or else CI will fail) but before the CLI release PR. Releases are built before
the PR is sent for review, so any changes after that point won't be included in the release and this cannot be combined with the
release PR.
AFTER a minor OR major CLI release:
constants.bzl also needs to be updated to use -next.0 after a major or minor release. However this needs to happen after FW
publishes the initial -next.0 release, which will happen 1 week after the major or minor release.
Releasing the CLI
Typical patch and next releases do not require FW to release in advance, as CLI does not pin the FW
dependency.
After confirming that the above steps have been done or are not necessary, run the following and
navigate the prompts:
pnpm ng-dev release publishReleases should be done in "reverse semver order", meaning they should follow:
Oldest LTS -> Newest LTS -> Patch -> RC -> Next
This can skip any versions which don't need releases, so most weeks are just "Patch -> Next".
Releasing a new package
Wombat has some special access requirements which need to be configured to publish a new NPM package.
See this Wombat doc
and this postmortem
for more info.
Angular is not an organization on NPM, therefore each package is published
independently and Wombat access needs to be managed individually. This also means
we can't rely on Wombat already having access to a new package.
In order to configure a brand new NPM package, it first needs to be published
manually so we can add Wombat access to it. Note that this step can and should be
done prior to weekly releases. The sooner this can be done, the less likely it
will block the next weekly release.
Check out the
mainbranch, which should always have a-nextversion.- This avoids having the initial publish actually be used in production.
Trigger a release build locally.
shellnvm install pnpm install --frozen-lockfile pnpm ng-dev release buildLog in to NPM as
angular.shellnpm login- See these two Valentine entries for authentication details:
Publish the release.
shell(cd dist/releases/my-scope/my-pkg/ && npm publish --access public)Add Wombat to the package.
shellnpm owner add google-wombot @my-scope/my-pkgDon't forget to logout.
shellnpm logoutFile a bug like b/336626936 to ask Wombat maintainers to
accept the invite for the new package.
Once Wombat accepts the invite, regular automated releases should work as expected.
Updating Browser Support
Angular's browser support is defined by a Baseline
"widely available" date. Before a new major version is released, this should be
updated to approximately the current date.
A few weeks before a major (around feature freeze):
- Update
BASELINE_DATEin/constants.bzlto the end of the most recent month.- For example, if it is currently May 12th, set
baselineThresholdto April
30th. - Picking a date at the end of a month makes it easier to cross-reference
Angular's support with other tools (like MDN) which state Baseline support
using month specificity. - Commit and merge the change, no other alterations or automation is
necessary in the CLI repo.
- For example, if it is currently May 12th, set
- Update the date in the
ng-packagrrepo./.stylesheet-processor.ts. - Update
angular.devdocumentation
to specify the date used and link to browsersl.ist
with the generated configuration.
File: docs/specifications/schematic-collections-config.md
Schematics Collections (`schematicCollections`)
The schematicCollections can be placed under the cli option in the global .angular.json configuration, at the root or at project level in angular.json .
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"schematicCollections": ["@schematics/angular", "@angular/material"]
}
// ...
}Rationale
When this option is not configured and a user would like to run a schematic which is not part of @schematics/angular,
the collection name needs to be provided to ng generate command in the form of [collection-name:schematic-name]. This make the ng generate command too verbose for repeated usages.
This is where the schematicCollections option can be useful. When adding @angular/material to the list of schematicCollections, the generate command will try to locate the schematic in the specified collections.
ng generate navigationis equivalent to:
ng generate @angular/material:navigationConflicting schematic names
When multiple collections have a schematic with the same name. Both ng generate and ng new will run the first schematic matched based on the ordering (as specified) of schematicCollections.
File: docs/specifications/schematic-prompts.md
Schematic Prompts
Schematic prompts provide the ability to introduce user interaction into the schematic execution. The schematic runtime supports the ability to allow schematic options to be configured to display a customizable question to the user and then use the response as the value for the option. These prompts are displayed before the execution of the schematic. This allows users direct the operation of the schematic without requiring indepth knowledge of the full spectrum of options available to the user.
To enable this capability, the JSON Schema used to define the schematic's options supports extensions to allow the declarative definition of the prompts and their respective behavior. No additional logic or changes are required to the JavaScript for a schematic to support the prompts.
Basic Usage
To illustrate the addition of a prompt to an existing schematic the following example JSON schema for a hypothetical hello world schematic will be used.
{
"properties": {
"name": {
"type": "string",
"minLength": 1,
"default": "world"
},
"useColor": {
"type": "boolean"
}
}
}Suppose it would be preferred if the user was asked for their name. This can be accomplished by augmenting the name property definition with an x-prompt field.
"x-prompt": "What is your name?"In most cases, only the text of the prompt is required. To minimize the amount of necessary configuration, the above shorthand form is supported and will typically be all that is required. Full details regarding the longhand form can be found in the Configuration Reference section.
Adding a prompt to allow the user to decided whether the schematic will use color when executing its hello action is also very similar. The schema with both prompts would be as follows:
{
"properties": {
"name": {
"type": "string",
"minLength": 1,
"default": "world",
"x-prompt": "What is your name?"
},
"useColor": {
"type": "boolean",
"x-prompt": "Would you like the response in color?"
}
}
}Prompts have several different types which provide the ability to display an input method that best represents the schematic option's potential values.
confirmation- A yes or no question; ideal for boolean optionsinput- textual input; ideal for string or number optionslist- a predefined set of items which may be selected
When using the shorthand form, the most appropriate type will automatically be selected based on the property's schema. In the example, the name prompt will use an input type because it is a string property. The useColor prompt will use a confirmation type because it is a boolean property with yes corresponding to true and no corresponding to false.
It is also important that the response from the user conforms to the constraints of the property. By specifying constraints using the JSON schema, the prompt runtime will automatically validate the response provided by the user. If the value is not acceptable, the user will be asked to enter a new value. This ensures that any values passed to the schematic will meet the expectations of the schematic's implementation and removes the need to add additional checks within the schematic's code.
Configuration Reference
The x-prompt field supports two alternatives to enable a prompt for a schematic option. A shorthand form when additional customization is not required and a longhand form providing the ability for more control over the prompt. All user responses are validated against the property's schema. For example, string type properties can use a minimum length or regular expression constraint to control the allowed values. In the event the response fails validation, the user will be asked to enter a new value.
Longhand Form
In the this form, the x-prompt field is an object with subfields that can be used to customize the behavior of the prompt. Note that some fields only apply to specific prompt types.
| Field | Data Value | Default |
|---|---|---|
type |
confirmation, input, list |
see shorthand section for details |
message |
string | N/A (required) |
items |
string and/or label/value object pair |
only valid with type list |
Shorthand Form
x-prompt [type: string] --> Question to display to the user.
For this usage, the type of the prompt is determined by the type of the containing property.
| Property Schema | Prompt Type | Notes |
|---|---|---|
"type": "boolean" |
confirmation |
|
"type": "string" |
input |
|
"type": "number" |
input |
only valid numbers accepted |
"type": "integer" |
input |
only valid numbers accepted |
"enum": [...] |
list |
enum members become list selections |
`x-prompt` Schema
{
"oneOf": [
{ "type": "string" },
{
"type": "object",
"properties": {
"type": { "type": "string" },
"message": { "type": "string" },
"items": {
"type": "array",
"items": {
"oneOf": [
{ "type": "string" },
{
"type": "object",
"properties": {
"label": { "type": "string" },
"value": {}
},
"required": ["value"]
}
]
}
}
},
"required": ["message"]
}
]
}