Compare commits

..

1 Commits

Author SHA1 Message Date
7d5c3eb001 chore(changelog): update change log to alpha.39 2015-10-06 12:13:35 -07:00
2697 changed files with 154786 additions and 164509 deletions

View File

@ -1,3 +1,3 @@
{
"directory" : "bower_components"
}
"directory": "bower_components"
}

6
.gitattributes vendored
View File

@ -1,9 +1,5 @@
# Auto detect text files and perform LF normalization
* text=auto
# JS and TS files must always use LF for tools to work
# JS files must always use LF for tools to work
*.js eol=lf
*.ts eol=lf
# Must keep Windows line ending to be parsed correctly
scripts/windows/packages.txt eol=crlf

View File

@ -1,39 +0,0 @@
<!--
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
-->
**I'm submitting a ...** (check one with "x")
```
[ ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
```
**Current behavior**
<!-- Describe how the bug manifests. -->
**Expected behavior**
<!-- Describe what the behavior would be without the bug. -->
**Minimal reproduction of the problem with instructions**
<!--
If the current behavior is a bug or you can illustrate your feature request better with an example,
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
-->
**What is the motivation / use case for changing the behavior?**
<!-- Describe the motivation or the concrete use case -->
**Please tell us about your environment:**
<!-- Operating system, IDE, package manager, HTTP server, ... -->
* **Angular version:** 2.0.X
<!-- Check whether this is still an issue in the most recent Angular version -->
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
<!-- All browsers where this could be reproduced -->
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
* **Node (for AoT issues):** `node --version` =

View File

@ -1,36 +0,0 @@
**Please check if the PR fulfills these requirements**
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)
**What kind of change does this PR introduce?** (check one with "x")
```
[ ] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:
```
**What is the current behavior?** (You can also link to an open issue here)
**What is the new behavior?**
**Does this PR introduce a breaking change?** (check one with "x")
```
[ ] Yes
[ ] No
```
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
**Other information**:

28
.gitignore vendored
View File

@ -1,14 +1,34 @@
.DS_STORE
# Dont commit the following directories created by pub.
packages
pubspec.lock
.pub
.packages
/dist/
.buildlog
node_modules
bower_components
# Or broccoli working directory
tmp
# Or the files created by dart2js.
*.dart.js
*.dart.precompiled.js
*.js_
*.js.deps
*.js.map
# Or type definitions we mirror from github
**/typings/**/*.d.ts
**/typings/tsd.cached.json
# Include when developing application packages.
pubspec.lock
.c9
.idea/
.settings/
*.swo
modules/.settings
.vscode
@ -20,8 +40,4 @@ modules/.vscode
# Ignore npm debug log
npm-debug.log
# build-analytics
.build-analytics
# rollup-test output
/modules/rollup-test/dist/
/docs/bower_components/

2
.nvmrc
View File

@ -1 +1 @@
6.6.0
0.12

12
.settings/settings.json Normal file
View File

@ -0,0 +1,12 @@
{
"search.exclude": {
".git" : true,
".idea": true,
"node_modules" : true,
"bower_components" : true,
"packages" : true,
"build" : true,
"dist" : true,
"tmp" : true
}
}

View File

@ -1,51 +1,128 @@
language: node_js
sudo: false
node_js:
- '6.6.0'
addons:
# firefox: "38.0"
apt:
sources:
# needed to install g++ that is used by npms's native modules
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- '4.1.1'
branches:
except:
- g3_v2_0
- g3sync
cache:
directories:
- ./node_modules
- ./.chrome/chromium
- node_modules
- $HOME/.pub-cache
env:
global:
# GITHUB_TOKEN_ANGULAR
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
- KARMA_BROWSERS=DartiumWithWebPlatform
- E2E_BROWSERS=Dartium
- LOGS_DIR=/tmp/angular-build/logs
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- ARCH=linux-x64
# TODO: change DART_DEV_VERSION to "latest" again once
# https://github.com/angular/angular/issues/4467 is solved
- DART_DEV_VERSION=1.13.0-dev.3.1
- DART_STABLE_VERSION=latest
# Token for tsd to increase github rate limit
# See https://github.com/DefinitelyTyped/tsd#tsdrc
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
# because those are not visible for pull requests, and those should also be reliable.
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
# (password is in Valentine)
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
# GITHUB_TOKEN_ANGULAR
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
matrix:
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
- CI_MODE=js
- CI_MODE=e2e
- CI_MODE=saucelabs_required
- CI_MODE=browserstack_required
- CI_MODE=saucelabs_optional
- CI_MODE=browserstack_optional
# Order: slowest build on top, so that we don't hog VMs while waiting for others to complete.
- MODE=dart DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
- MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=js DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=router DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
matrix:
fast_finish: true
allow_failures:
- env: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional"
- env: "MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
- env: "MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
addons:
firefox: "38.0"
before_install:
- echo ${TSDRC} > .tsdrc
- export DISPLAY=:99.0
- export GIT_SHA=$(git rev-parse HEAD)
- ./scripts/ci/init_android.sh
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
- sh -e /etc/init.d/xvfb start
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
install:
- ./scripts/ci-lite/install.sh
# Update npm
- npm install -g npm@2.14.5
- npm --version
# Check the size of caches
- du -sh ./node_modules || true
# Install npm dependecies
- npm install
before_script:
- mkdir -p $LOGS_DIR
- ./scripts/ci/presubmit-queue-setup.sh
script:
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
- ./scripts/ci/build_and_test.sh ${MODE}
after_script:
- ./scripts/ci-lite/cleanup.sh
- ./scripts/ci/print-logs.sh
- ./scripts/ci/after-script.sh
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# trigger Buildtime Trend Service to parse Travis CI log
- https://buildtimetrend.herokuapp.com/travis
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
slack:
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
deploy:
- provider: gcs
# This is for project angular-github-babysitter
access_key_id: GOOGIOQTDBEOPBUAWFZQ
secret_access_key:
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
# this bucket has a lifecycle to delete after 90 days:
# $ echo '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 90}}]}' > lifecycle.json
# $ gsutil lifecycle set lifecycle.json gs://angular2-snapshots
bucket: angular2-snapshots
# don't delete generated files
skip_cleanup: true
# serve to public at https://storage.googleapis.com/angular2-snapshots/SHA/dart_stable/dist.tgz
acl: public-read
# upload the .tgz archive created in scripts/ci/build_and_test.sh
local-dir: deploy
# create a "subdirectory" for each commit
upload-dir: $TRAVIS_COMMIT/dart_stable
on:
repo: angular/angular
condition: "$MODE = dart && $DART_CHANNEL = stable"
- provider: gcs
access_key_id: GOOGIOQTDBEOPBUAWFZQ
secret_access_key:
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
bucket: angular2-snapshots
skip_cleanup: true
acl: public-read
local-dir: deploy
upload-dir: $TRAVIS_COMMIT/js
on:
repo: angular/angular
condition: "$MODE = js"

File diff suppressed because it is too large Load Diff

View File

@ -11,11 +11,8 @@ Someone with committer access will do the rest.
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
Angular committers and also prevent breakages on master.
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
so the caretaker reviews the changes manually.
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
When a PR is ready to merge, a project member in the CoreTeamMember list (see below) can add the special label,
`PR: merge`.
A robot running as [mary-poppins](https://github.com/mary-poppins)
is notified that the label was added by an authorized person,
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
@ -29,6 +26,6 @@ Finally, after merge `mary-poppins` removes the presubmit branch.
## Administration
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
The list of users who can trigger a merge by adding the label is stored in our appengine app datastore.
Edit the contents of the [CoreTeamMember Table](
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)

View File

@ -18,17 +18,18 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
## <a name="question"></a> Got a Question or Problem?
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code, you can help us by
If you find a bug in the source code or a mistake in the documentation, you can help us by
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
[submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> Want a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
a proposal for your work first, to be sure that we can use it.
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
and we are not ready to accept major contributions ahead of the full release.
Please consider what kind of change it is:
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
@ -47,18 +48,14 @@ features, by not reporting duplicate issues. Providing the following informatio
chances of your issue being dealt with quickly:
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
* **Motivation for or Use Case** - explain why this is a bug for you
* **Browsers and Operating System** - is this a problem with all browsers?
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps.
* **Related Issues** - has a similar issue been reported before?
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
causing the problem (line of code or commit)
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
@ -94,7 +91,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
* In GitHub, send a pull request to `angular:master`.
* If we suggest changes then:
* Make the required updates.
* Re-run the Angular 2 test suites to ensure tests are still passing.
* Re-run the Angular 2 test suites for JS and Dart to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
@ -165,19 +162,6 @@ The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
```
docs(changelog): update change log to beta.5
```
```
fix(release): need to depend on latest rxjs and zone.js
The version in our package.json gets copied to the one we publish, and users need the latest of these.
```
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
@ -191,10 +175,9 @@ Must be one of the following:
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests or correcting existing tests
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
* **chore**: Other changes that don't modify `src` or `test` files
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
generation
### Scope
The scope could be anything specifying place of the commit change. For example
@ -237,8 +220,8 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
[github]: https://github.com/angular/angular
[gitter]: https://gitter.im/angular/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
[jsfiddle]: http://jsfiddle.net
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[jsfiddle]: http://jsfiddle.net/
[plunker]: http://plnkr.co/edit
[runnable]: http://runnable.com
[runnable]: http://runnable.com/
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular

View File

@ -1,13 +1,18 @@
# Building and Testing Angular 2 for JS
# Building and Testing Angular 2 for JS and Dart
This document describes how to set up your development environment to build and test Angular 2 JS version.
It also explains the basic mechanics of using `git`, `node`, and `npm`.
This document describes how to set up your development environment to build and test Angular, both
JS and Dart versions. It also explains the basic mechanics of using `git`, `node`, and `npm`.
* [Prerequisite Software](#prerequisite-software)
* [Getting the Sources](#getting-the-sources)
* [Installing NPM Modules](#installing-npm-modules)
* [Building](#building)
* [Environment Variable Setup](#environment-variable-setup)
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
* [Running Tests Locally](#running-tests-locally)
* [Formatting](#clang-format)
* [Project Information](#project-information)
* [CI using Travis](#ci-using-travis)
* [Transforming Dart code](#transforming-dart-code)
* [Debugging](#debugging)
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
if you'd like to contribute to Angular.
@ -17,17 +22,30 @@ if you'd like to contribute to Angular.
Before you can build and test Angular, you must install and configure the
following products on your development machine:
* [Dart](https://www.dartlang.org) (version ` >=1.12.0 <2.0.0`), specifically the Dart-SDK and
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
download [page for instructions](https://www.dartlang.org/tools/download.html).
You can also download both **stable** and **dev** channel versions from the [download
archive](https://www.dartlang.org/tools/download-archive). In that case, on Windows, Dart must be added
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
Git](https://help.github.com/articles/set-up-git) is a good source of information.
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) which is used to run a development web server,
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
* [Node.js](http://nodejs.org), (version `>=4.1.1 <5`) which is used to run a development web server,
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
(version `>=2.14.5 <3.0`), which comes with Node. Depending on your system, you can install Node either from
source or as a pre-packaged bundle.
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
to execute the selenium standalone server for e2e testing.
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
bleeding edge functionality, built especially for developers (and early adopters).
* [Bower](http://bower.io/).
## Getting the Sources
@ -50,9 +68,36 @@ cd angular
# Add the main Angular repository as an upstream remote to your repository:
git remote add upstream https://github.com/angular/angular.git
```
## Installing NPM Modules
Next, install the JavaScript modules needed to build and test Angular:
## Environment Variable Setup
Define the environment variables listed below. These are mainly needed for the testing. The
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
your favorite shell.
Examples given below of possible values for initializing the environment variables assume **Mac OS
X** and that you have installed the Dart Editor in the directory named by
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
```shell
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
```
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
[here](https://www.dartlang.org/tools/pub/installing.html)):
```shell
# DART_SDK: path to a Dart SDK directory
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
# Update PATH to include the Dart SDK bin directory
PATH+=":$DART_SDK/bin"
```
## Installing NPM Modules and Dart Packages
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
```shell
# Install Angular project dependencies (package.json)
@ -74,67 +119,219 @@ use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Windows only
## Build commands
To build Angular and prepare tests, run:
In order to create the right symlinks, run **as administrator**:
```shell
./scripts/windows/create-symlinks.sh
$(npm bin)/gulp build
```
Before submitting a PR, do not forget to remove them:
```shell
./scripts/windows/remove-symlinks.sh
```
Notes:
* Results are put in the `dist` folder.
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`.
## Building
You can selectively build either the JS or Dart versions as follows:
To build Angular run:
* `$(npm bin)/gulp build.js`
* `$(npm bin)/gulp build.dart`
Also note that in order for the whole test suite to succeed you will need to generate the type definitions by running:
```shell
./build.sh
$(npm bin)/gulp docs/typings
```
* Results are put in the dist folder.
To clean out the `dist` folder, run:
```shell
$(npm bin)/gulp clean
```
## Running Tests Locally
To run tests:
### Full test suite
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests
that run on Travis.
You can selectively run either the JS or Dart versions as follows:
* `$(npm bin)/gulp test.all.js`
* `$(npm bin)/gulp test.all.dart`
### Unit tests
You can run just the unit tests as follows:
* `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e.
watches the test files for changes and re-runs tests when files are updated).
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
* `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
If you prefer running tests in "single-run" mode rather than watch mode use:
* `$(npm bin)/gulp test.unit.js/ci`
* `$(npm bin)/gulp test.unit.cjs/ci`
* `$(npm bin)/gulp test.unit.dart/ci`
The task updates the dist folder with transpiled code whenever a source or test file changes, and
Karma is run against the new output.
**Note**: If you want to only run a single test you can alter the test you wish to run by changing
`it` to `iit` or `describe` to `ddescribe`. This will only run that individual test and make it
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
tests respectively.
**Note**: **watch mode** needs symlinks to work, so if you're using windows, ensure you have the
rights to built them in your operating system.
### E2E tests
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver).
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js e2e tests.
Angular specific command line options when running protractor:
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
### Performance tests
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver)
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: dart2js performance tests
Angular specific command line options when running protractor (e.g. force gc, ...):
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
## Formatting with <a name="clang-format">clang-format</a>
We use [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to automatically enforce code
style for our TypeScript code. This allows us to focus our code reviews more on the content, and
less on style nit-picking. It also lets us encode our style guide in the `.clang-format` file in the
repository, allowing many tools and editors to share our settings.
To check the formatting of your code, run
gulp check-format
Note that the continuous build on Travis runs `gulp enforce-format`. Unlike the `check-format` task,
this will actually fail the build if files aren't formatted according to the style guide.
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
to some whitespace difference.
* Install clang-format with `npm install -g clang-format`.
* Use `clang-format -i [file name]` to format a file (or multiple).
Note that `clang-format` tries to load a `clang-format` node module close to the sources being
formatted, or from the `$CWD`, and only then uses the globally installed one - so the version used
should automatically match the one required by the project.
Use `clang-format -version` in case you get confused.
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives
you a command line to format your code.
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
touched.
* You can run this as a **git pre-commit hook** to automatically format your delta regions when you
commit a change. In the angular repo, run
```
$ echo -e '#!/bin/sh\nexec git clang-format' > .git/hooks/pre-commit
$ chmod u+x !$
```
* **WebStorm** can run clang-format on the current file.
1. Under Preferences, open Tools > External Tools.
1. Plus icon to Create Tool
1. Fill in the form:
- Name: clang-format
- Description: Format
- Synchronize files after execution: checked
- Open console: not checked
- Show in: Editor menu
- Program: [path to clang-format, try `$ echo $(npm config get prefix)/bin/clang-format`]
- Parameters: `-i -style=file $FilePath$`
- Working directory: `$ProjectFileDir$`
* `clang-format` integrations are also available for many popular editors (`vim`, `emacs`,
`Sublime Text`, etc.).
## Generating the API documentation
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
```shell
$ ./test.sh node # Run all angular tests on node
$ ./test.sh browser # Run all angular tests in browser
$ ./test.sh browserNoRouter # Optionally run all angular tests without router in browser
$ ./test.sh tools # Run angular tooling (not framework) tests
$(npm bin)/gulp docs/angular.io
```
You should execute the 3 test suites before submitting a PR to github.
You can serve the generated documentation to check how it would render on [angular.io](https://angular.io/):
- check out the [angular.io repo](https://github.com/angular/angular.io) locally,
- install dependencies as described in the [angular.io README](https://github.com/angular/angular.io/blob/master/README.md),
- copy the generated documentation from your local angular repo at `angular/dist/angular.io/partials/api/angular2` to your local angular.io repo at `angular.io/public/docs/js/latest/api`,
- run `harp compile` at the root of the angular.io repo to check the generated documentation for errors,
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
## Project Information
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
### Folder structure
- CircleCI fails if your code is not formatted properly,
- Travis CI fails if any of the test suites described above fails.
* `modules/*`: modules that will be loaded in the browser
* `tools/*`: tools that are needed to build Angular
* `dist/*`: build files are placed here.
## Update the public API tests
### File suffixes
If you happen to modify the public API of Angular, API golden files must be updated using:
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
* `*.dart`: Dart files that don't get transpiled
``` shell
$ gulp public-api:update
```
## CI using Travis
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
For instructions on setting up Continuous Integration using Travis, see the instructions given
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
## Formatting your source code
## Transforming Dart code
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
is not properly formatted, the CI will fail and the PR can not be merged.
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
You can automatically format your code by running:
## Debugging
``` shell
$ gulp format
```
### Debug the transpiler
If you need to debug the transpiler:
- add a `debugger;` statement in the transpiler code,
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
debugger
- press "c" to execute the program until you reach the `debugger;` statement,
- you can then type "repl" to enter the REPL and inspect variables in the context.
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
Notes:
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
code you want to debug (the former will process the "modules" folder while the later processes
the transpiler specs).
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
the developer tools are opened in the browser running Karma.
### Debug the tests
If you need to debug the tests:
- add a `debugger;` statement to the test you want to debug (or the source code),
- execute karma `$(npm bin)/gulp test.js`,
- press the top right "DEBUG" button,
- open the DevTools and press F5,
- the execution halts at the `debugger;` statement
**Note (WebStorm users)**:
1. Create a Karma run config from WebStorm.
2. Then in the "Run" menu, press "Debug 'karma-js.conf.js'", and WebStorm will stop in the generated
code on the `debugger;` statement.
3. You can then step into the code and add watches.
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
the original source files are not supported at the moment.

215
LICENSE
View File

@ -1,21 +1,202 @@
The MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2014-2016 Google, Inc. http://angular.io
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,37 +1,43 @@
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/angular/tree/master)
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/angular2.svg)](http://badge.fury.io/js/angular2)
[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci)
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
Angular
Angular
=========
Angular is a development platform for building mobile and desktop web applications. This is the
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
Angular 2 is currently in **Developer Preview**. We recommend using Angular 1.X for production
applications:
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
## Quickstart
[Get started in 5 minutes][quickstart].
## Setup & Install Angular 2
Follow the instructions given on the [Angular download page][download].
## Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
[browserstack]: https://www.browserstack.com/
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
[dart]: http://www.dartlang.org
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
[dartium]: http://www.dartlang.org/tools/dartium
[download]: http://angular.io/download/
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
[ng2]: http://angular.io
[ngDart]: http://angulardart.org
[ngJS]: http://angularjs.org
[ng2dart]: https://github.com/dart-lang/angular2

140
TOOLS.md
View File

@ -1,140 +1,4 @@
# Developer Tools for Angular 2
Here you will find a collection of tools and tips for keeping your application
perform well and contain fewer bugs.
## Angular debug tools in the dev console
Angular provides a set of debug tools that are accessible from any browser's
developer console. In Chrome the dev console can be accessed by pressing
Ctrl + Shift + j.
### Enabling debug tools
By default the debug tools are disabled. You can enable debug tools as follows:
```typescript
import {enableDebugTools} from '@angular/platform-browser';
bootstrap(Application).then((appRef) => {
enableDebugTools(appRef);
});
```
### Using debug tools
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
top level object is called `ng` and contains more specific tools inside it.
Example:
```javascript
ng.profiler.timeChangeDetection();
```
## Performance
### Change detection profiler
If your application is janky (it misses frames) or is slow according to other
metrics it is important to find the root cause of the issue. Change detection
is a phase in Angular's lifecycle that detects changes in values that are
bound to UI, and if it finds a change it performs the corresponding UI update.
However, sometimes it is hard to tell if the slowness is due to the act of
computing the changes being slow, or due to the act of applying those changes
to the UI. For your application to be performant it is important that the
process of computing changes is very fast. For best results it should be under
3 milliseconds in order to leave room for the application logic, the UI updates
and browser's rendering pipeline to fit withing the 16 millisecond frame
(assuming the 60 FPS target frame rate).
Change detection profiler repeatedly performs change detection without invoking
any user actions, such as clicking buttons or entering text in input fields. It
then computes the average amount of time it took to perform a single cycle of
change detection in milliseconds and prints it to the console. This number
depends on the current state of the UI. You will likely see different numbers
as you go from one screen in your application to another.
#### Running the profiler
Enable debug tools (see above), then in the dev console enter the following:
```javascript
ng.profiler.timeChangeDetection();
```
The results will be printed to the console.
#### Recording CPU profile
Pass `{record: true}` an argument:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then open the "Profiles" tab. You will see the recorded profile titled
"Change Detection". In Chrome, if you record the profile repeatedly, all the
profiles will be nested under "Change Detection".
#### Interpreting the numbers
In a properly-designed application repeated attempts to detect changes without
any user actions should result in no changes to be applied on the UI. It is
also desirable to have the cost of a user action be proportional to the amount
of UI changes required. For example, popping up a menu with 5 items should be
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
change detection with no UI updates should be as fast as possible. Ideally the
number printed by the profiler should be well below the length of a single
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
#### Investigating slow change detection
So you found a screen in your application on which the profiler reports a very
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
recording while profiling:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then look for hot spots using
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
#### Reducing change detection cost
There are many reasons for slow change detection. To gain intuition about
possible causes it would help to understand how change detection works. Such a
discussion is outside the scope of this document (TODO link to docs), but here
are some key concepts in brief.
By default Angular uses "dirty checking" mechanism for finding model changes.
This mechanism involves evaluating every bound expression that's active on the
UI. These usually include text interpolation via `{{expression}}` and property
bindings via `[prop]="expression"`. If any of the evaluated expressions are
costly to compute they could contribute to slow change detection. A good way to
speed things up is to use plain class fields in your expressions and avoid any
kinds of computation. Example:
```typescript
@Component({
template: '<button [enabled]="isEnabled">{{title}}</button>'
})
class FancyButton {
// GOOD: no computation, just return the value
isEnabled: boolean;
// BAD: computes the final value upon request
_title: String;
get title(): String { return this._title.trim().toUpperCase(); }
}
```
Most cases like these could be solved by precomputing the value and storing the
final value in a field.
Angular also supports a second type of change detection - the "push" model. In
this model Angular does not poll your component for changes. Instead, the
component "tells" Angular when it changes and only then does Angular perform
the update. This model is suitable in situations when your data model uses
observable or immutable objects (also a discussion for another time).
- [JavaScript](TOOLS_JS.md)
- [Dart](TOOLS_DART.md)

324
TOOLS_DART.md Normal file
View File

@ -0,0 +1,324 @@
# Developer Tools for Dart
Use these tools and techniques to increase your app's performance
and reliability.
* [Angular debugging tools](#angular-debugging-tools)
* [Code size](#code-size)
* [Performance](#performance)
## Angular debugging tools
Starting with alpha.38, Angular provides a set of debugging tools
that are accessible from any browser's developer console.
In Chrome, you can get to the dev console by pressing
Ctrl + Shift + J (on Mac: Cmd + Opt + J).
### Enabling the debugging tools
By default the debugging tools are disabled.
Enable the debugging tools as follows:
```dart
import 'package:angular2/tools.dart';
main() async {
var appRef = await bootstrap(Application);
enableDebugTools(appRef);
}
```
<!-- Change function name to enableDebuggingTools? -->
### Using the debugging tools
In the browser, open the dev console. The top-level object is called `ng` and
contains more specific tools inside it.
For example, to run the change detection profiler on your app:
<!-- QUESTION: is "on your app" accurate?
is "run the change detection profiler on your app" the best wording? -->
```javascript
// In the dev console:
ng.profiler.timeChangeDetection();
```
The [Change detection profiler](#change-detection-profiler) section
has more details.
<!-- Point to API docs when they're published, if they're useful.
They should be under
http://www.dartdocs.org/documentation/angular2/latest
and/or
https://angular.io/docs/js/latest/api/. -->
## Code size
Code must be downloaded, parsed, and executed. Too much code can lead to
slow application start-up time, especially on slow networks and low-end devices.
The tools and techniques in this section can help you to identify
unnecessarily large code and to reduce code size.
### Finding contributors to code size
Options for investigating code size include the `--dump-info` dart2js option,
ng2soyc, `reflector.trackUsage()`, and code coverage information
from the Dart VM.
#### --dump-info
The `--dump-info` option of `dart2js` outputs information about what happened
during compilation. You can specify `--dump-info` in `pubspec.yaml`:
```yaml
transformers:
...
- $dart2js:
commandLineOptions:
- --dump-info
```
The [Dump Info Visualizer](https://github.com/dart-lang/dump-info-visualizer)
can help you analyze the output.
For more information, see the
[dart2js_info API reference](http://dart-lang.github.io/dart2js_info/doc/api/).
#### ng2soyc.dart
[ng2soyc](https://github.com/angular/ng2soyc.dart) is a utility for analyzing
code size contributors in Angular 2 applications. It groups code size by
library and, assuming your library names follow
[standard naming conventions](https://www.dartlang.org/articles/style-guide/#do-prefix-library-names-with-the-package-name-and-a-dot-separated-path)
(package.library.sublibrary...), gives the code size breakdown at
each level. To reduce noise in the output of very large apps, ng2soyc provides
an option to hide libraries that are too small, so you can focus on the biggest
contributors.
#### Track unused reflection data
<!-- QUESTION: How do you get access to reflector & ReflectionInfo? -->
Call `reflector.trackUsage()` to track reflection information used
by the application. Reflection information (`ReflectionInfo`) is a data
structure that stores information about your application that Angular uses for
locating DI factories, generated change detectors and other code related to a
given type. After exercising your application, call `reflector.listUnusedKeys()`
to get a list of types and functions whose reflection information was retained
but never used by the application.
#### Use code coverage to find dead code
When running in Dartium (or in the Dart VM, in general) you can request code
coverage information from the VM. You can either use
[observatory](https://www.dartlang.org/tools/observatory/) or download
the coverage file and use your own tools to inspect it. Lines of code that are
not covered are top candidates for dead code.
Keep in mind, however, that uncovered code is not sufficient evidence of dead
code, only necessary evidence. It is perfectly possible that you simply didn't
exercise your application in a way that triggers the execution of uncovered
code. A common example is error handling code. Just because your testing never
encountered an error does not mean the error won't happen in production. You
therefore don't have to rush and remove all the `catch` blocks.
### Reducing code size
To reduce code size, you can disable reflection,
enable minification, and manually remove dead code.
You can also try less safe options such as
telling dart2js to trust type annotations.
#### Disable reflection
`dart:mirrors` allows discovering program metadata at runtime. However, this
means that `dart2js` needs to retain that metadata and thus increase the size
of resulting JS output. In practice, however, it is possible to extract most
metadata necessary for your metaprogramming tasks statically using a
transformer and `package:analyzer`, and act on it before compiling to JS.
#### Enable minification
Minification shortens all your `longMethodNames` into 2- or 3-letter long
symbols. `dart2js` ensures that this kind of renaming is done safely, without
breaking the functionality of your programs. You can enable it in `pubspec.yaml`
under `$dart2js` transformer:
```yaml
transformers:
...
- $dart2js:
minify: true
```
#### Manually remove dead code
`dart2js` comes with dead code elimination out-of-the-box. However, it may not
always be able to tell if a piece of code could be used. Consider the following
example:
```dart
/// This function decides which serialization format to use
void setupSerializers() {
if (server.doYouSupportProtocolBuffers()) {
useProtobufSerializers();
} else {
useJsonSerializers();
}
}
```
In this example the application asks the server what kind of serialization
format it uses and dynamically chooses one or the other. `dart2js` can't
tell whether the server responds with yes or no, so it must retain both
kinds of serializers. However, if you know that your server supports
protocol buffers, you can remove that `if` block entirely and default to
protocol buffers.
Code coverage (see above) is a good way to find dead code in your app.
#### Unsafe options
Dart also provides more aggressive optimization options. However, you have to
be careful when using them and as of today the benefits aren't that clear. If
your type annotations are inaccurate you may end up with non-Darty runtime
behavior, including the classic "undefined is not a function" tautology, as
well as the "keep on truckin'" behavior, e.g. `null + 1 == 1` and
`{} + [] == 0`.
`--trust-type-annotations` tells `dart2js` to trust that your type annotations
are correct. So if you have a function `foo(Bar bar)` the compiler can omit the
check that `bar` is truly `Bar` when calling methods on it.
`--trust-primitives` tells `dart2js` that primitive types, such as numbers and
booleans are never `null` when performing arithmetic, and that your program
does not run into range error when operating on lists, letting the compiler
remove some of the error checking code.
Specify these options in `pubspec.yaml`.
Example:
```yaml
transformers:
...
- $dart2js:
commandLineOptions:
- --trust-type-annotations
- --trust-primitives
```
## Performance
### Change detection profiler
If your application is janky (it misses frames) or is slow according to other
metrics it is important to find the root cause of the issue. Change detection
is a phase in Angular's lifecycle that detects changes in values that are
bound to UI, and if it finds a change it performs the corresponding UI update.
However, sometimes it is hard to tell if the slowness is due to the act of
computing the changes being slow, or due to the act of applying those changes
to the UI. For your application to be performant it is important that the
process of computing changes is very fast. For best results it should be under
3 milliseconds in order to leave room for the application logic, the UI updates
and browser's rendering pipeline to fit withing the 16 millisecond frame
(assuming the 60 FPS target frame rate).
Change detection profiler repeatedly performs change detection without invoking
any user actions, such as clicking buttons or entering text in input fields. It
then computes the average amount of time it took to perform a single cycle of
change detection in milliseconds and prints it to the console. This number
depends on the current state of the UI. You are likely to see different numbers
as you go from one screen in your application to another.
#### Running the profiler
Enable the debugging tools (see above),
then in the dev console enter the following:
```javascript
ng.profiler.timeChangeDetection();
```
The results are printed to the console.
#### Recording CPU profiles
To record a profile, pass `{record: true}` to `timeChangeDetection()`:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then open the **Profiles** tab. The recorded profile has the title
**Change Detection**. In Chrome, if you record the profile repeatedly, all the
profiles are nested under Change Detection.
#### Interpreting the numbers
In a properly designed application, repeated attempts to detect changes without
any user actions result in no changes to the UI. It is
also desirable to have the cost of a user action be proportional to the amount
of UI changes required. For example, popping up a menu with 5 items should be
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
change detection with no UI updates should be as fast as possible. Ideally the
number printed by the profiler should be well below the length of a single
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
#### Investigating slow change detection
So you found a screen in your application on which the profiler reports a very
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
recording while profiling:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then look for hot spots using
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
#### Reducing change detection cost
There are many reasons for slow change detection. To gain intuition about
possible causes it helps to understand how change detection works. Such a
discussion is outside the scope of this document,
but here are some key concepts.
<!-- TODO: link to change detection docs -->
By default, Angular uses a _dirty checking_ mechanism to find model changes.
This mechanism involves evaluating every bound expression that's active on the
UI. These usually include text interpolation via `{{expression}}` and property
bindings via `[prop]="expression"`. If any of the evaluated expressions are
costly to compute, they might contribute to slow change detection. A good way to
speed things up is to use plain class fields in your expressions and avoid any
kind of computation. For example:
```dart
@View(
template: '<button [enabled]="isEnabled">{{title}}</button>'
)
class FancyButton {
// GOOD: no computation, just returns the value
bool isEnabled;
// BAD: computes the final value upon request
String _title;
String get title => _title.trim().toUpperCase();
}
```
Most cases like these can be solved by precomputing the value and storing the
final value in a field.
Angular also supports a second type of change detection: the _push_ model. In
this model, Angular does not poll your component for changes. Instead, the
component tells Angular when it changes, and only then does Angular perform
the update. This model is suitable in situations when your data model uses
observable or immutable objects.
<!-- TODO: link to discussion of push model -->

140
TOOLS_JS.md Normal file
View File

@ -0,0 +1,140 @@
# Developer Tools for JavaScript
Here you will find a collection of tools and tips for keeping your application
perform well and contain fewer bugs.
## Angular debug tools in the dev console
Angular provides a set of debug tools that are accessible from any browser's
developer console. In Chrome the dev console can be accessed by pressing
Ctrl + Shift + j.
### Enabling debug tools
By default the debug tools are disabled. You can enable debug tools as follows:
```typescript
import 'angular2/tools';
bootstrap(Application).then((appRef) => {
enableDebugTools(appRef);
});
```
### Using debug tools
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
top level object is called `ng` and contains more specific tools inside it.
Example:
```javascript
ng.profiler.timeChangeDetection();
```
## Performance
### Change detection profiler
If your application is janky (it misses frames) or is slow according to other
metrics it is important to find the root cause of the issue. Change detection
is a phase in Angular's lifecycle that detects changes in values that are
bound to UI, and if it finds a change it performs the corresponding UI update.
However, sometimes it is hard to tell if the slowness is due to the act of
computing the changes being slow, or due to the act of applying those changes
to the UI. For your application to be performant it is important that the
process of computing changes is very fast. For best results it should be under
3 milliseconds in order to leave room for the application logic, the UI updates
and browser's rendering pipeline to fit withing the 16 millisecond frame
(assuming the 60 FPS target frame rate).
Change detection profiler repeatedly performs change detection without invoking
any user actions, such as clicking buttons or entering text in input fields. It
then computes the average amount of time it took to perform a single cycle of
change detection in milliseconds and prints it to the console. This number
depends on the current state of the UI. You will likely see different numbers
as you go from one screen in your application to another.
#### Running the profiler
Enable debug tools (see above), then in the dev console enter the following:
```javascript
ng.profiler.timeChangeDetection();
```
The results will be printed to the console.
#### Recording CPU profile
Pass `{record: true}` an argument:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then open the "Profiles" tab. You will see the recorded profile titled
"Change Detection". In Chrome, if you record the profile repeatedly, all the
profiles will be nested under "Change Detection".
#### Interpreting the numbers
In a properly-designed application repeated attempts to detect changes without
any user actions should result in no changes to be applied on the UI. It is
also desirable to have the cost of a user action be proportional to the amount
of UI changes required. For example, popping up a menu with 5 items should be
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
change detection with no UI updates should be as fast as possible. Ideally the
number printed by the profiler should be well below the length of a single
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
#### Investigating slow change detection
So you found a screen in your application on which the profiler reports a very
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
recording while profiling:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then look for hot spots using
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
#### Reducing change detection cost
There are many reasons for slow change detection. To gain intuition about
possible causes it would help to understand how change detection works. Such a
discussion is outside the scope of this document (TODO link to docs), but here
are some key concepts in brief.
By default Angular uses "dirty checking" mechanism for finding model changes.
This mechanism involves evaluating every bound expression that's active on the
UI. These usually include text interpolation via `{{expression}}` and property
bindings via `[prop]="expression"`. If any of the evaluated expressions are
costly to compute they could contribute to slow change detection. A good way to
speed things up is to use plain class fields in your expressions and avoid any
kinds of computation. Example:
```typescript
@View({
template: '<button [enabled]="isEnabled">{{title}}</button>'
})
class FancyButton {
// GOOD: no computation, just return the value
isEnabled: boolean;
// BAD: computes the final value upon request
_title: String;
get title(): String { return this._title.trim().toUpperCase(); }
}
```
Most cases like these could be solved by precomputing the value and storing the
final value in a field.
Angular also supports a second type of change detection - the "push" model. In
this model Angular does not poll your component for changes. Instead, the
component "tells" Angular when it changes and only then does Angular perform
the update. This model is suitable in situations when your data model uses
observable or immutable objects (also a discussion for another time).

View File

@ -1,72 +1,25 @@
# Triage Process and Github Labels for Angular 2
This document describes how the Angular team uses labels and milestones
to triage issues on github. The basic idea of the new process is that
caretaker only assigns a component and type (bug, feature) label. The
owner of the component than is in full control of how the issues should
be triaged further.
This document describes how the Angular team uses labels and milestones to triage issues on github.
Once this process is implemented and in use, we will revisit it to see
if further labeling is needed.
# Issues and PRs
## Triaged vs Untriaged Issues
## Components
Every triaged issue must have four attributes assigned to it:
A caretaker should be able to determine which component the issue
belongs to. The components have a clear piece of source code associated
with it.
* `priority` -- P0 through P4. P0 issues are "drop everything and do this now". P4 are nice to have.
* `component` -- Which area of Angular knowledge this relates to.
* `effort` -- Rough assessment of how much work this issue is. E.g. `effort: easy` means
"probably a few hours of work".
* `type` -- Whether this issue is a bug, feature, or other kind of task.
* `comp: animations`: `@matsko`
* `comp: benchpress`: `@tbosch`
* `comp: build & ci`: `@IgorMinar` -- All build and CI scripts
* `comp: common`: `@mhevery` -- This includes core components / pipes.
* `comp: core & compiler`: `@tbosch` -- Because core and compiler are very
intertwined, we will be treating them as one.
* `comp: forms`: `@kara`
* `comp: http`: `@jeffbcross`
* `comp: i18n`: `@vicb`
* `comp: metadata-extractor`: `@chuckjaz`
* `comp: router`: `@vsavkin`
* `comp: testing`: `@juliemr`
* `comp: upgrade`: `@mhevery`
* `comp: web-worker`: `@vicb`
* `comp: zones`: `@mhevery`
Untriaged issues are any issues in the queue that don't yet have these four attributes.
There are few components which are cross-cutting. They don't have
a clear location in the source tree. We will treat them as a component
even thought no specific source tree is associated with them.
You can view a report of untriaged issues here, in our
[Angular Triage Dashboard](http://mhevery.github.io/github_issues/).
* `comp: documentation`: `@naomiblack`
* `comp: packaging`: `@IgorMinar`
* `comp: performance`: `@tbosch`
* `comp: security`: `@IgorMinar`
## Type
What kind of problem is this?
* `type: RFC / discussion / question`
* `type: bug`
* `type: chore`
* `type: feature`
* `type: performance`
* `type: refactor`
## Caretaker Triage Process
It is the caretaker's responsibility to assign `comp: *` and `type: *`
to each new issue as they come in. The reason why we limit the
responsibility of the caretaker to these two labels is that it is
unlikely that without domain knowledge the caretaker could add any
additional labels of value.
## Component's owner Triage Process
At this point we are leaving each component owner to determine their own
process for their component.
It will be up to the component owner to determine the order in which the
issues within the component will be resolved.
Issues should also have a clear action to complete that can be addressed or resolved within the
scope of Angular 2. We'll close issues that don't meet these criteria.
### Assigning Issues to Milestones
@ -84,10 +37,7 @@ We aim to only have at most three milestones open at a time:
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
consists of all issues that have been triaged but do not have an assignee or milestone.
## Triaged vs Untrained PRs
PRs should also be label with a `comp: *` so that it is clear which
primary area the PR effects.
## Triaged vs Untriaged PRs
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
closing or reviewing PRs is a top priority ahead of other ongoing work.
@ -113,6 +63,90 @@ uncontroversial change.
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
PR to land.
Victor (`vsavkin`) and Tobias (`tbosch`) are owners of the PR queue. Here is a list of [current
untriaged PRs](https://github.com/angular/angular/pulls?utf8=%E2%9C%93&q=is%3Aopen+no%3Amilestone+is%3Apr+-label%3A%22pr_action%3A+cleanup%22+-label%3A%22pr_action%3A+merge%22+-label%3A%22pr_action%3A+review%22+-label%3A%22pr_action%3A+discuss%22+-label%3A%22pr_state%3A+blocked%22+-label%3A%22pr_state%3A+WIP%22+).
# Prioritization of Work
What should you be working on?
1. Any PRs that are assigned to you that don't have `pr_state: WIP` or `pr_state: blocked`
1. Any issues that are assigned to you in the lowest-numbered Milestone
1. Any issues that are assigned to you in any Milestone
If there are no issues assigned to you in any Milestone, pick an issue, self-assign it, and add
it to the most appropriate Milestone based on effort.
Here are some suggestions for what to work on next:
* Filter for issues in a component that you are knowledgeable about, and pick something that has a
high priority.
* Filter for any small effort task that has the special `cust: GT` or `cust:Ionic` tags,
and priority > P3.
* Add a new task that's really important, add `component`, `priority`, `effort`, `type` and
assign it to yourself and the most appropriate milestone.
# Labels Used in Triage
## Priority
How urgent is this issue? We use priority to determine what should be worked on in each new
milestone.
* `P0: critical` -- drop everything to work on this
* `P1: urgent` -- resolve quickly in the current milestone. people are blocked
* `P2: required` -- needed for development but not urgent yet. workaround exists, or e.g. new API
* `P3: important` -- must complete before Angular 2 is ready for release
* `P4: nice to have` -- a good idea, but maybe not until after release
## Effort
Rough, non-binding estimate of how much work this issue represents. Please change this assessment
for anything you're working on to better reflect reality.
* `effort: easy` -- straightforward issue that can be resolved in a few hours, e.g. < 1 day of work.
* `effort: medium` -- issue that will be a few days of work. Can be completed within a single
milestone.
* `effort: tough` -- issue that will likely take more than 1 milestone to complete.
<!-- We don't like these label names as
they're not absolute (what is one developer-hour, really?) but decided it wasn't worth arguing
over terms. -->
## Component
Which area of Angular knowledge is this issue most closely related to? Helpful when deciding what
to work on next.
* `comp: benchpress` -- benchmarks and performance testing &rarr; *tbosch*, *crossj*
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks &rarr; *iminar*, *caitp*
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart &rarr; *mprobst*, *alexeagle*
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) &rarr;
*mhevery*
* `comp: core/animations` -- animations framework &rarr; *matsko*
* `comp: core/change_detection` -- change detection &rarr; *vsavkin*
* `comp: core/di` -- dependency injection &rarr; *vicb*, *rkirov*
* `comp: core/directives` -- directives
* `comp: core/forms` -- forms &rarr; *vsavkin*
* `comp: core/pipes` -- pipes
* `comp: core/view` -- runtime processing of the `View`s
* `comp: core/view/compiler` -- static analysis of the templates which generate `ProtoView`s.
* `comp: core/testbed` -- e2e tests and support for them
* `comp: core/webworker` -- core web worker infrastructure
* `comp: dart-transformer` -- Dart transforms &rarr; *kegluneq*, *jakemac*
* `comp: data-access` -- &rarr; *jeffbcross*
* `comp: docs` -- API docs and doc generation &rarr; *naomiblack*, *petebacondarwin*
* `comp: material-components` -- Angular Material components built in Angular 2 &rarr; *jelbourn*
* `comp: router` -- Component Router &rarr; *btford*, *igorminar*, *matsko*
* `comp: wrenchjs`
## Type
What kind of problem is this?
* `type RFC / discussion / question`
* `type bug`
* `type chore`
* `type feature`
* `type performance`
* `type refactor`
## Special Labels
@ -126,6 +160,9 @@ More active discussion is needed before the issue can be worked on further. Typi
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
`cla:yes` should be merged into master.
### cust
This is an issue causing user pain for early adopter customers `cust: GT` or `cust: Ionic`.
### WORKS_AS_INTENDED
Only used on closed issues, to indicate to the reporter why we closed it.

View File

@ -1,6 +1,6 @@
{
"name": "angular2",
"dependencies": {
"polymer": "Polymer/polymer#^1.6.0"
"polymer": "dart-lang/polymer_js#0.8.0-preview"
}
}

View File

@ -1,177 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL)
// and BrowserStack (BS).
// If the target is set to null, then the browser is not run anywhere during CI.
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented
// out in Travis configuration.
var CIconfiguration = {
'Chrome': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Firefox': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
// Currently deactivated due to https://github.com/angular/angular/issues/7560
'ChromeBeta': {unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
'FirefoxBeta': {unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
'ChromeDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'IE9': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Edge': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Android4.1': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.2': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.3': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.4': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android5': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Safari7': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
};
var customLaunchers = {
'DartiumWithWebPlatform':
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
'SL_SAFARI8': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.10', version: '8.0'},
'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'},
'SL_SAFARI10':
{base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.0'},
'SL_IOS7': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '7.1'},
'SL_IOS8': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '8.4'},
'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'},
'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
'SL_IE9':
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
'SL_IE10': {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 2012',
version: '10'
},
'SL_IE11':
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
'SL_EDGE': {
base: 'SauceLabs',
browserName: 'MicrosoftEdge',
platform: 'Windows 10',
version: '13.10586'
},
'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.1'},
'SL_ANDROID4.2': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.2'},
'SL_ANDROID4.3': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.3'},
'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'},
'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.1'},
'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
'BS_SAFARI7': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Mavericks'},
'BS_SAFARI8': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Yosemite'},
'BS_SAFARI9': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'El Capitan'},
'BS_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'},
'BS_IOS7': {base: 'BrowserStack', device: 'iPhone 5S', os: 'ios', os_version: '7.0'},
'BS_IOS8': {base: 'BrowserStack', device: 'iPhone 6', os: 'ios', os_version: '8.3'},
'BS_IOS9': {base: 'BrowserStack', device: 'iPhone 6S', os: 'ios', os_version: '9.1'},
'BS_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'},
'BS_IE9':
{base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'},
'BS_IE10': {
base: 'BrowserStack',
browser: 'ie',
browser_version: '10.0',
os: 'Windows',
os_version: '8'
},
'BS_IE11': {
base: 'BrowserStack',
browser: 'ie',
browser_version: '11.0',
os: 'Windows',
os_version: '10'
},
'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'},
'BS_WINDOWSPHONE':
{base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.1'},
'BS_ANDROID5': {base: 'BrowserStack', device: 'Google Nexus 5', os: 'android', os_version: '5.0'},
'BS_ANDROID4.4': {base: 'BrowserStack', device: 'HTC One M8', os: 'android', os_version: '4.4'},
'BS_ANDROID4.3':
{base: 'BrowserStack', device: 'Samsung Galaxy S4', os: 'android', os_version: '4.3'},
'BS_ANDROID4.2':
{base: 'BrowserStack', device: 'Google Nexus 4', os: 'android', os_version: '4.2'},
'BS_ANDROID4.1':
{base: 'BrowserStack', device: 'Google Nexus 7', os: 'android', os_version: '4.1'}
};
var sauceAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {
return customLaunchers[item].base == 'SauceLabs';
}),
'DESKTOP': [
'SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7',
'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'
],
'MOBILE': [
'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7',
'SL_IOS8', 'SL_IOS9', 'SL_IOS10'
],
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false)
};
var browserstackAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {
return customLaunchers[item].base == 'BrowserStack';
}),
'DESKTOP': [
'BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7',
'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'
],
'MOBILE': [
'BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'
],
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
};
module.exports = {
customLaunchers: customLaunchers,
sauceAliases: sauceAliases,
browserstackAliases: browserstackAliases
};
function buildConfiguration(type, target, required) {
return Object.keys(CIconfiguration)
.filter((item) => {
var conf = CIconfiguration[item][type];
return conf.required === required && conf.target === target;
})
.map((item) => target + '_' + item.toUpperCase());
}

165
build.sh
View File

@ -1,165 +0,0 @@
#!/usr/bin/env bash
set -e -o pipefail
cd `dirname $0`
PACKAGES=(core
compiler
common
forms
platform-browser
platform-browser-dynamic
platform-server
platform-webworker
platform-webworker-dynamic
http
router
upgrade
compiler-cli
benchpress)
BUILD_ALL=true
BUNDLE=true
for ARG in "$@"; do
case "$ARG" in
--packages=*)
PACKAGES_STR=${ARG#--packages=}
PACKAGES=( ${PACKAGES_STR//,/ } )
BUILD_ALL=false
;;
--bundle=*)
BUNDLE=( "${ARG#--bundle=}" )
;;
*)
echo "Unknown option $ARG."
exit 1
;;
esac
done
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
TSCONFIG=./tools/tsconfig.json
echo "====== (tools)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
rm -rf ./dist/tools/
mkdir -p ./dist/tools/
$(npm bin)/tsc -p ${TSCONFIG}
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
if [[ ${BUILD_ALL} == true ]]; then
rm -rf ./dist/all/
mkdir -p ./dist/all/
echo "====== Copying files needed for e2e tests ====="
cp -r ./modules/playground ./dist/all/
cp -r ./modules/playground/favicon.ico ./dist/
#rsync -aP ./modules/playground/* ./dist/all/playground/
mkdir ./dist/all/playground/vendor
cd ./dist/all/playground/vendor
ln -s ../../../../node_modules/core-js/client/core.js .
ln -s ../../../../node_modules/zone.js/dist/zone.js .
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
ln -s ../../../../node_modules/base64-js/lib/b64.js .
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
ln -s ../../../../node_modules/rxjs .
ln -s ../../../../node_modules/angular/angular.js .
cd -
echo "====== Copying files needed for benchmarks ====="
cp -r ./modules/benchmarks ./dist/all/
cp -r ./modules/benchmarks/favicon.ico ./dist/
mkdir ./dist/all/benchmarks/vendor
cd ./dist/all/benchmarks/vendor
ln -s ../../../../node_modules/core-js/client/core.js .
ln -s ../../../../node_modules/zone.js/dist/zone.js .
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
ln -s ../../../../node_modules/base64-js/lib/b64.js .
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
ln -s ../../../../node_modules/rxjs .
ln -s ../../../../node_modules/angular/angular.js .
ln -s ../../../../bower_components/polymer .
ln -s ../../../../node_modules/incremental-dom/dist/incremental-dom-cjs.js
cd -
TSCONFIG=./modules/tsconfig.json
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
# compile ts code
$TSC -p modules/tsconfig.json
rm -rf ./dist/packages-dist
fi
for PACKAGE in ${PACKAGES[@]}
do
PWD=`pwd`
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
rm -rf ${DESTDIR}
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
$TSC -p ${SRCDIR}/tsconfig-build.json
cp ${SRCDIR}/package.json ${DESTDIR}/
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"
$TSC -p ${SRCDIR}/tsconfig-testing.json
fi
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
if [ "$(uname)" == "Darwin" ]; then
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\/\/\/ <reference types="node" \/>//g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
else
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\/\/\/ <reference types="node" \/>//g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
fi
if [[ ${PACKAGE} == benchpress ]]; then
cp ${SRCDIR}/*.md ${DESTDIR}
cp -r ${SRCDIR}/docs ${DESTDIR}
fi
if [[ ${BUNDLE} == true && ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
echo "====== BUNDLING: ${SRCDIR} ====="
mkdir ${DESTDIR}/bundles
(
cd ${SRCDIR}
echo "====== Rollup ${PACKAGE} index"
../../../node_modules/.bin/rollup -c rollup.config.js
cat ${LICENSE_BANNER} > ${UMD_ES5_PATH}.tmp
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
if [[ -e rollup-testing.config.js ]]; then
echo "====== Rollup ${PACKAGE} testing"
../../../node_modules/.bin/rollup -c rollup-testing.config.js
echo "{\"main\": \"../bundles/${PACKAGE}-testing.umd.js\"}" > ${DESTDIR}/testing/package.json
cat ${LICENSE_BANNER} > ${UMD_TESTING_ES5_PATH}.tmp
cat ${UMD_TESTING_ES5_PATH} >> ${UMD_TESTING_ES5_PATH}.tmp
mv ${UMD_TESTING_ES5_PATH}.tmp ${UMD_TESTING_ES5_PATH}
fi
) 2>&1 | grep -v "as external dependency"
fi
done
./modules/@angular/examples/build.sh

View File

@ -1,11 +0,0 @@
machine:
node:
version: 5.4.1
dependencies:
pre:
- npm install -g npm
test:
override:
- gulp lint

View File

@ -0,0 +1,76 @@
var path = require('canonical-path');
var Package = require('dgeni').Package;
var basePackage = require('../public-docs-package');
var PARTIAL_PATH = 'partials';
var MODULES_DOCS_PATH = PARTIAL_PATH + '/api';
module.exports = new Package('angular.io', [basePackage])
.factory(require('./services/renderMarkdown'))
.processor(require('./processors/addJadeDataDocsProcessor'))
.processor(require('./processors/filterUnwantedDecorators'))
// Configure rendering
.config(function(templateFinder, templateEngine) {
templateFinder.templateFolders
.unshift(path.resolve(__dirname, 'templates'));
})
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/angular.io';
})
.config(function(readFilesProcessor, generateNavigationDoc, createOverviewDump) {
// Clear out unwanted processors
readFilesProcessor.$enabled = false;
generateNavigationDoc.$enabled = false;
createOverviewDump.$enabled = false;
})
.config(function(filterUnwantedDecorators, log) {
log.level = 'info';
filterUnwantedDecorators.decoratorsToIgnore = [
'CONST',
'IMPLEMENTS',
'ABSTRACT'
];
})
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
computePathsProcessor.pathTemplates.push({
docTypes: ['module'],
pathTemplate: '${id}/',
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.jade'
});
computePathsProcessor.pathTemplates.push({
docTypes: EXPORT_DOC_TYPES,
pathTemplate: '${moduleDoc.id}/${name}-${docType}.html',
outputPathTemplate: MODULES_DOCS_PATH + '/${moduleDoc.id}/${name}-${docType}.jade',
});
computePathsProcessor.pathTemplates.push({
docTypes: ['jade-data'],
pathTemplate: '${originalDoc.id}/_data',
outputPathTemplate: MODULES_DOCS_PATH + '/${path}.json'
});
})
.config(function(getLinkInfo) {
getLinkInfo.relativeLinks = true;
})
.config(function(templateEngine, getInjectables) {
templateEngine.filters = templateEngine.filters.concat(getInjectables([
require('./rendering/trimBlankLines'),
require('./rendering/toId')
]));
});

View File

@ -0,0 +1,8 @@
var Package = require('dgeni').Package;
module.exports = function mockPackage() {
return new Package('mockPackage', [require('../')])
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
};

View File

@ -0,0 +1,86 @@
var _ = require('lodash');
var path = require('canonical-path');
var titleCase = function(text) {
return text.replace(/(.)(.*)/, function(_, first, rest) {
return first.toUpperCase() + rest;
});
};
/*
* Create _data.json file for Harp pages
*
* http://harpjs.com/docs/development/metadata
*
* This method creates the meta data required for each page
* such as the title, description, etc. This meta data is used
* in the harp static site generator to create the title for headers
* and the navigation used in the API docs
*
*/
module.exports = function addJadeDataDocsProcessor() {
return {
$runAfter: ['adding-extra-docs'],
$runBefore: ['extra-docs-added'],
$process: function(docs) {
var extraDocs = [];
var modules = [];
/*
* Create Data for Modules
*
* Modules must be public and have content
*/
_.forEach(docs, function(doc) {
if (doc.docType === 'module' && !doc.private && doc.exports.length) {
modules.push(doc);
// GET DATA FOR INDEX PAGE OF MODULE SECTION
var indexPageInfo = [{
name: 'index',
title: _.map(path.basename(doc.fileInfo.baseName).split('_'), function(part) {
return titleCase(part);
}).join(' '),
intro: doc.description.replace('"', '\"').replace(/\s*(\r?\n|\r)\s*/g," "),
docType: 'module'
}];
// GET DATA FOR EACH PAGE (CLASS, VARS, FUNCTIONS)
var modulePageInfo = _(doc.exports)
.map(function(exportDoc) {
var dataDoc = {
name: exportDoc.name + '-' + exportDoc.docType,
title: exportDoc.name,
docType: exportDoc.docType
};
if (exportDoc.symbolTypeName) dataDoc.varType = titleCase(exportDoc.symbolTypeName);
if (exportDoc.originalModule) dataDoc.originalModule = exportDoc.originalModule;
return dataDoc;
})
.sortBy('name')
.value();
//COMBINE PAGE DATA
var allPageData = indexPageInfo.concat(modulePageInfo);
// PUSH DATA TO EXTRA DOCS ARRAY
extraDocs.push({
id: doc.id + "-data",
aliases: [doc.id + "-data"],
docType: 'jade-data',
originalDoc: doc,
data: allPageData
});
}
});
return docs.concat(extraDocs);
}
};
};

View File

@ -0,0 +1,66 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
describe('addJadeDataDocsProcessor', function() {
var dgeni, injector, processor;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
processor = injector.get('addJadeDataDocsProcessor');
});
it('should add a doc for each module', function() {
var docs = [
{
docType: 'module',
id: 'someModule',
exports: [
{ name: 'someObj', docType: 'var', symbolTypeName: 'MyClass', originalModule: 'some/private/module' }
],
fileInfo: { baseName: 'x_y' },
description: 'some description\nsecond line'
}
];
docs = processor.$process(docs);
expect(docs[1]).toEqual({
id : 'someModule-data',
aliases : [ 'someModule-data' ],
docType : 'jade-data',
originalDoc : docs[0],
data : [
{ name : 'index', title : 'X Y', intro : 'some description second line', docType : 'module' },
{ name : 'someObj-var', title : 'someObj', varType : 'MyClass', docType: 'var', originalModule: 'some/private/module' }
] });
});
it('should sort the exports into alphabetical order', function() {
var docs = [
{
docType: 'module',
id: 'someModule',
exports: [
{ name: 'Beta', docType: 'class'},
{ name: 'Alpha', docType: 'class'},
{ name: 'Gamma', docType: 'class'},
{ name: 'Nu', docType: 'class'},
{ name: 'Mu', docType: 'class'}
],
fileInfo: { baseName: 'x_y' },
description: 'some description\nsecond line'
}
];
docs = processor.$process(docs);
expect(docs[1].data).toEqual([
{ name : 'index', title : 'X Y', intro : 'some description second line', docType : 'module' },
{ name: 'Alpha-class', title: 'Alpha', docType: 'class' },
{ name: 'Beta-class', title: 'Beta', docType: 'class' },
{ name: 'Gamma-class', title: 'Gamma', docType: 'class' },
{ name: 'Mu-class', title: 'Mu', docType: 'class' },
{ name: 'Nu-class', title: 'Nu', docType: 'class' }
]);
});
});

View File

@ -0,0 +1,20 @@
var _ = require('lodash');
module.exports = function filterUnwantedDecorators() {
return {
decoratorsToIgnore: [],
$runAfter: ['processing-docs'],
$runBefore: ['docs-processed'],
$process: function(docs) {
var decoratorsToIgnore = this.decoratorsToIgnore || [];
_.forEach(docs, function(doc) {
if (doc.decorators) {
doc.decorators = _.filter(doc.decorators, function(decorator) {
return decoratorsToIgnore.indexOf(decorator.name) === -1;
});
}
});
return docs;
}
};
}

View File

@ -0,0 +1,46 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
describe('filterUnwantedDecorators', function() {
var dgeni, injector, processor;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
processor = injector.get('filterUnwantedDecorators');
});
it('should remove decorators specified by name', function() {
var docs = [
{ id: 'doc1', decorators: [ { name: 'A' }, { name: 'B' } ] },
{ id: 'doc2', decorators: [ { name: 'B' }, { name: 'C' } ] },
{ id: 'doc3', decorators: [ { name: 'A' }, { name: 'C' } ] }
];
processor.decoratorsToIgnore = ['D', 'B'];
docs = processor.$process(docs);
expect(docs).toEqual([
{ id: 'doc1', decorators: [ { name: 'A' } ] },
{ id: 'doc2', decorators: [ { name: 'C' } ] },
{ id: 'doc3', decorators: [ { name: 'A' }, { name: 'C' } ] }
]);
});
it('should ignore docs that have no decorators', function() {
var docs = [
{ id: 'doc1', decorators: [ { name: 'A' }, { name: 'B' } ] },
{ id: 'doc2' },
{ id: 'doc3', decorators: [ { name: 'A' }, { name: 'C' } ] }
];
processor.decoratorsToIgnore = ['D', 'B'];
docs = processor.$process(docs);
expect(docs).toEqual([
{ id: 'doc1', decorators: [ { name: 'A' } ] },
{ id: 'doc2' },
{ id: 'doc3', decorators: [ { name: 'A' }, { name: 'C' } ] }
]);
});
});

View File

@ -0,0 +1,8 @@
module.exports = function() {
return {
name: 'toId',
process: function(str) {
return str.replace(/[^(a-z)(A-Z)(0-9)._-]/, '-');
}
};
};

View File

@ -0,0 +1,12 @@
module.exports = function() {
return {
name: 'trimBlankLines',
process: function(str) {
var lines = str.split(/\r?\n/);
while(lines.length && (lines[0].trim() === '')) {
lines.shift();
}
return lines.join('\n');
}
};
};

View File

@ -0,0 +1,18 @@
var factory = require('./trimBlankLines');
describe('trimBlankLines filter', function() {
var filter;
beforeEach(function() {
filter = factory();
});
it('should be called "trimBlankLines"', function() {
expect(filter.name).toEqual('trimBlankLines');
});
it('should remove all empty lines from the start of the string', function() {
expect(filter.process('\n\n\nsome text\n\nmore text\n\n'))
.toEqual('some text\n\nmore text\n\n');
});
});

View File

@ -0,0 +1,54 @@
var marked = require('marked');
var Encoder = require('node-html-encoder').Encoder;
var html2jade = require('html2jade');
var indentString = require('indent-string');
function stripTags(s) { //from sugar.js
return s.replace(RegExp('<\/?[^<>]*>', 'gi'), '');
}
// entity type encoder
var encoder = new Encoder('entity');
/**
* @dgService renderMarkdown
* @description
* Render the markdown in the given string as HTML.
*/
module.exports = function renderMarkdown(trimIndentation) {
var renderer = new marked.Renderer();
renderer.code = function(code, lang, escaped) {
var cssClasses = ['prettyprint', 'linenums'];
var trimmedCode = trimIndentation(code);
if(lang) {
if(lang=='html') {
trimmedCode = encoder.htmlEncode(trimmedCode);
}
cssClasses.push(this.options.langPrefix + escape(lang, true));
}
return 'pre(class="' + cssClasses.join(' ') + '")\n' + indentString('code.\n', ' ', 2) + trimmedCode;
};
renderer.heading = function (text, level, raw) {
var headingText = marked.Renderer.prototype.heading.call(renderer, text, level, raw);
var title = 'h2 ' + stripTags(headingText);
if (level==2) {
title = '.l-main-section\n' + indentString(title, ' ', 2) ;
}
return title;
};
return function(content) {
return marked(content, { renderer: renderer });
};
};

View File

@ -0,0 +1,62 @@
{% include "lib/githubLinks.html" -%}
{% include "lib/paramList.html" -%}
{% extends 'layout/base.template.html' -%}
{% block body %}
p.location-badge.
exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
defined in {$ githubViewLink(doc) $}
:markdown
{$ doc.description | indent(2, true) | trimBlankLines $}
{%- if doc.decorators %}
.l-main-section
h2 Annotations
{%- for decorator in doc.decorators %}
.l-sub-section
h3.annotation {$ decorator.name $}
pre.prettyprint
code.
@{$ decorator.name $}{$ paramList(decorator.arguments) | indent(8, false) $}
{% endfor %}
{% endif -%}
{%- if doc.constructorDoc or doc.members.length -%}
.l-main-section
h2 Members
{%- if doc.constructorDoc %}
.l-sub-section
h3#{$ doc.constructorDoc.name | toId $} {$ doc.constructorDoc.name $}
{% if doc.constructorDoc.parameters %}
pre.prettyprint
code.
{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.parameters) | indent(8, false) | trim $}
{% endif %}
:markdown
{$ doc.constructorDoc.description | indent(6, true) | replace('## Example', '') | replace('# Example', '') | trimBlankLines $}
{% endif -%}
{%- for member in doc.members %}{% if not member.private %}
.l-sub-section
h3#{$ member.name | toId $} {$ member.name $}{% if member.optional %}?{% endif %}
{% if member.parameters %}
pre.prettyprint
code.
{$ member.name $}{$ paramList(member.parameters) | indent(8, false) | trim $}{$ returnType(doc.returnType) $}
{% endif %}
:markdown
{$ member.description | indent(6, true) | replace('## Example', '') | replace('# Example', '') | trimBlankLines $}
{% endif %}{% endfor %}
{%- endif -%}
{% endblock %}

View File

@ -0,0 +1 @@
{% extends 'class.template.html' -%}

View File

@ -0,0 +1,22 @@
{% include "lib/githubLinks.html" -%}
{% include "lib/paramList.html" -%}
{% extends 'layout/base.template.html' -%}
{% block body %}
.l-main-section
h2(class="function export") {$ doc.name $}
{% if doc.parameters %}
pre.prettyprint
code.
{$ doc.name $}{$ paramList(doc.parameters) | indent(4, true) | trim $}{$ returnType(doc.returnType) $}
{% endif %}
p.location-badge.
exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
defined in {$ githubViewLink(doc) $}
:markdown
{$ doc.description | indent(4, true) | trimBlankLines $}
{% endblock %}

View File

@ -0,0 +1,17 @@
{
{%- for item in doc.data %}
"{$ item.name $}" : {
"title" : "{$ item.title $}",
{%- if item.intro %}
"intro" : "{$ item.intro $}",
{%- endif %}
{%- if item.varType %}
"varType" : "{$ item.varType $}",
{%- endif %}
{%- if item.originalModule %}
"originalModule" : "{$ item.originalModule $}",
{%- endif %}
"docType": "{$ item.docType $}"
}{% if not loop.last %},{% endif %}
{% endfor -%}
}

View File

@ -0,0 +1 @@
{% block body %}{% endblock %}

View File

@ -0,0 +1,12 @@
{% macro paramList(params) -%}
{%- if params -%}
({%- for param in params -%}
{$ param | escape $}{% if not loop.last %}, {% endif %}
{%- endfor %})
{%- endif %}
{%- endmacro -%}
{% macro returnType(returnType) -%}
{%- if returnType %} : {$ returnType | escape $}{% endif -%}
{%- endmacro -%}

View File

@ -0,0 +1,15 @@
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' -%}
{% block body -%}
p.location-badge.
defined in {$ githubViewLink(doc) $}
ul
for page, slug in public.docs[current.path[1]][current.path[2]][current.path[3]][current.path[4]]._data
if slug != 'index'
- var url = "/docs/" + current.path[1] + "/" + current.path[2] + "/" + current.path[3] + "/" + current.path[4] + "/" + slug + ".html"
li.c8
!= partial("../../../../../_includes/_hover-card", {name: page.title, url: url })
{% endblock %}

View File

@ -0,0 +1,12 @@
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' %}
{% block body %}
.l-main-section
p.location-badge.
exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
defined in {$ githubViewLink(doc) $}
:markdown
{$ doc.description | indent(4, true) | trimBlankLines $}
{% endblock %}

379
docs/app/css/app.css Normal file
View File

@ -0,0 +1,379 @@
.hide { display: none !important; }
body {
overflow: hidden;
max-width: 100%;
max-height: 100%;
font-size: 14px;
}
a {
color: #3f51b5;
}
table {
margin-bottom: 20px;
max-width: 100%;
width: 100%;
border-spacing: 0;
border-collapse: collapse;
background-color: transparent;
}
td,
th {
padding: $baseline-grid ($baseline-grid * 2);
border-top: 1px solid #ddd;
vertical-align: top;
}
th {
border-bottom: 2px solid #ddd;
vertical-align: bottom;
}
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
.md-sidenav-inner {
background: #fff;
}
.layout-content,
.doc-content {
max-width: 864px;
margin: auto;
}
.layout-label {
width: 120px;
}
.layout-content code.highlight {
margin-bottom: 15px;
}
.menu-item {
background: none;
border-width: 0;
cursor: pointer;
display: block;
color: #333;
font-size: inherit;
line-height: 40px;
max-height: 40px;
opacity: 1;
margin: 0;
outline: none;
padding: 0px 28px;
position: relative;
text-align: left;
text-decoration: none;
width: 100%;
z-index: 1;
-webkit-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
-webkit-transition-property: max-height, background-color, opacity;
-moz-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
-moz-transition-property: max-height, background-color, opacity;
transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
transition-property: max-height, background-color, opacity;
}
.menu-item.ng-hide {
max-height: 0;
opacity: 0;
}
.menu-item:hover {
color: #999;
}
.menu-item:focus {
font-weight: bold;
}
.menu-item.menu-title {
color: #888;
font-size: 14px;
padding-left: 16px;
text-align: left;
text-transform: uppercase;
transition: color 0.35s cubic-bezier(0.35, 0, 0.25, 1);
}
.menu-item.menu-title:hover,
.menu-item.menu-title.active {
color: #1976d2;
}
.menu-icon {
background: none;
border: none;
}
.app-toolbar .md-toolbar-tools h3 {
-webkit-margin-before: 0;
-webkit-margin-after: 0;
}
.demo-container {
border-radius: 4px;
margin-top: 16px;
-webkit-transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
position: relative;
padding-bottom: 0;
}
.demo-source-tabs {
z-index: 1;
-webkit-transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
max-height: 448px;
min-height: 448px;
background: #fff;
overflow: hidden;
}
md-tabs.demo-source-tabs md-tab,
md-tabs.demo-source-tabs .md-header {
background-color: #444444 !important;
}
md-tabs.demo-source-tabs md-tab-label {
color: #ccc !important;
}
md-tabs.demo-source-tabs .active md-tab-label {
color: #fff !important;
}
.demo-source-tabs.ng-hide {
max-height: 0px;
min-height: 0px;
}
.demo-source-tabs {
position: relative;
width: 100%;
z-index: 0;
}
.demo-content {
position: relative;
overflow:hidden;
min-height: 448px;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
}
.small-demo .demo-source-tabs:not(.ng-hide) {
min-height: 224px;
max-height: 224px;
}
.small-demo .demo-content {
min-height: 128px;
}
.demo-content > * {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
}
.demo-content > div[layout-fill] {
min-height: 448px;
}
.small-demo .demo-content > div[layout-fill] {
min-height: 224px;
}
.small-demo .demo-toolbar,
.small-demo .md-toolbar-tools {
min-height: 48px;
max-height: 48px;
font-size: 20px;
}
.show-source md-toolbar.demo-toolbar {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.36);
}
.demo-toolbar .md-button {
color: #616161;
}
md-toolbar.demo-toolbar,
.demo-source-tabs md-tab,
.demo-source-tabs .tabs-header {
background: #E0E0E0 !important;
color: #616161;
}
md-toolbar.demo-toolbar md-tab-label {
color: #99E4EE
}
md-toolbar.demo-toolbar .md-button:hover,
md-toolbar.demo-toolbar .md-button:focus,
md-toolbar.demo-toolbar .md-button.active {
background: rgba(0,0,0,0.1);
}
md-toolbar.demo-toolbar .md-button {
-webkit-transition: all 0.3s linear;
-moz-transition: all 0.3s linear;
transition: all 0.3s linear;
}
.demo-source-container {
display: block;
border: 1px solid #ddd;
background-color: #f6f6f6;
height: 400px;
}
.demo-source-content {
height: 400px;
}
.demo-source-content,
.demo-source-content pre,
.demo-source-content code {
background: #f6f6f6;
font-family: monospace;
}
.demo-source-content pre {
max-width: 100%;
overflow-wrap: break-word;
}
.show-source div[demo-include] {
border-top: #ddd solid 2px;
}
.menu-separator-icon {
margin: 0;
}
.menu-module-name {
opacity: 0.6;
font-size: 18px;
}
/************
* DOCS
************/
.api-options-bar .md-button {
margin: 4px;
padding: 4px;
}
.api-options-bar .md-button:hover,
.api-options-bar .md-button:focus {
background: rgba(0, 0, 0, 0.2);
}
.api-options-bar.with-icon md-icon {
position: absolute;
top: -3px;
left: 2px;
}
.api-options-bar.with-icon .md-button span {
margin-left: 22px;
}
.api-params-item {
min-height: 72px;
border-bottom: 1px solid #ddd;
}
.api-params-label {
margin-right: 8px;
text-align: center;
margin-top: 14px;
-webkit-align-self: flex-start;
-moz-align-self: flex-start;
-ms-flex-item-align: start;
align-self: flex-start;
}
.api-params-title {
color: #888;
}
code.api-type {
font-weight: bold;
}
ul {
margin: 0;
}
ul li {
margin-top: 3px;
list-style-position: inside;
}
ul li:first-child {
margin-top: 0;
}
.layout-title {
color: #999999;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
}
.api-params-content ul {
padding-left: 4px;
}
ul.methods > li {
margin-bottom: 48px;
}
ul.methods .method-function-syntax {
font-weight: normal;
font-size: 20px;
margin: 0;
-webkit-margin-before: 0;
-webkit-margin-after: 0;
}
ul.methods li h3 {
/* border-bottom: 1px solid #eee; */
}
@media (max-width: 600px) {
ul.methods > li {
padding-left: 0;
border-left: none;
list-style: default;
}
ul.methods .method-function-syntax {
font-size: 14px;
}
}
.version {
padding-left: 10px;
text-decoration: underline;
font-size: 0.95em;
}
.demo-source-container pre,
.demo-source-container code {
min-height: 100%;
}
md-content.demo-source-container > hljs > pre > code.highlight {
position : absolute;
top : 0px;
left: 0px;
right: 0px;
}
.extraPad {
padding-left:32px !important;
padding-right:32px !important;
}
.member .name {
white-space: pre-wrap;
word-wrap: break-word;
font-family: monospace;
font-size: 1.17em;
margin: 1em 0;
}
.left-nav {
min-width: 300px;
}

View File

@ -0,0 +1,142 @@
/* GitHub Theme */
.prettyprint {
background: white;
font-family: Menlo, 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Consolas, monospace;
font-size: 12px;
line-height: 1.5;
}
.lang-text * {
color: #333333!important;
}
.pln {
color: #333333;
}
@media screen {
.str {
color: #dd1144;
}
.kwd {
color: #333333;
}
.com {
color: #999988;
}
.typ {
color: #445588;
}
.lit {
color: #445588;
}
.pun {
color: #333333;
}
.opn {
color: #333333;
}
.clo {
color: #333333;
}
.tag {
color: navy;
}
.atn {
color: teal;
}
.atv {
color: #dd1144;
}
.dec {
color: #333333;
}
.var {
color: teal;
}
.fun {
color: #990000;
}
}
@media print, projection {
.str {
color: #006600;
}
.kwd {
color: #006;
font-weight: bold;
}
.com {
color: #600;
font-style: italic;
}
.typ {
color: #404;
font-weight: bold;
}
.lit {
color: #004444;
}
.pun, .opn, .clo {
color: #444400;
}
.tag {
color: #006;
font-weight: bold;
}
.atn {
color: #440044;
}
.atv {
color: #006600;
}
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0;
}
/* IE indents via margin-left */
li.L0,
li.L1,
li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
/* */
}
/* Alternate shading for lines */
li.L1,
li.L3,
li.L5,
li.L7,
li.L9 {
/* */
}

55
docs/app/index.html Normal file
View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Angular 2 Docs</title>
<base href="/">
<link rel="stylesheet" type="text/css" href="/lib/angular-material/angular-material.css">
<link rel="stylesheet" type="text/css" href="/css/prettify-theme.css">
<link rel="stylesheet" type="text/css" href="/css/app.css">
<script src="/lib/hammerjs/hammer.js"></script>
<script src="/lib/google-code-prettify/src/prettify.js"></script>
<script src="/lib/google-code-prettify/src/lang-css.js"></script>
<script src="/lib/angular/angular.js"></script>
<script src="/lib/angular-animate/angular-animate.js"></script>
<script src="/lib/angular-aria/angular-aria.js"></script>
<script src="/lib/angular-material/angular-material.js"></script>
<script src="/js/navigation-modules.js"></script>
<script src="/js/navigation-guides.js"></script>
<script src="/js/app.js"></script>
<script src="/js/code.js"></script>
</head>
<body ng-app="app" ng-controller="NavController as nav" layout="column">
<md-toolbar md-scroll-shrink>
<h1 class="md-toolbar-tools">Angular V2</h1>
</md-toolbar>
<section layout="row">
<md-content class="left-nav">
<h2>Navigation</h2>
<section ng-repeat="area in nav.areas">
<h3>{{ area.name }}</h3>
<md-list>
<md-item ng-repeat="section in area.sections">
<h3><a href="{{section.path}}">{{section.name}}</a></h3>
<ul>
<li ng-repeat="page in section.pages">
<a href="{{page.path}}">{{ page.name }}</a>
</li>
</ul>
</md-item>
</md-list>
</section>
</md-content>
<md-content class="md-padding">
<ng-include autoscroll src="nav.currentPage.partial"></ng-include>
</md-content>
</section>
</body>
</html>

46
docs/app/js/app.js Normal file
View File

@ -0,0 +1,46 @@
angular.module('app', ['ngMaterial', 'navigation-modules', 'navigation-guides', 'code'])
.config(function($locationProvider) {
$locationProvider.html5Mode(true);
})
.controller('NavController', ['$scope', '$location', 'MODULES', 'GUIDES',
function($scope, $location, MODULES, GUIDES) {
var that = this;
this.areas = [
{ name: 'Guides', sections: [ { pages: GUIDES.pages } ] },
{ name: 'Modules', sections: MODULES.sections }
];
this.updateCurrentPage = function(path) {
console.log('path', path);
this.currentPage = null;
this.areas.forEach(function(area) {
area.sections.forEach(function(section) {
// Short-circuit out if the page has been found
if ( that.currentPage ) {
return;
}
if (section.path === path) {
console.log('found!');
that.currentPage = section;
} else {
section.pages.forEach(function(page) {
if (page.path === path) {
that.currentPage = page;
}
});
}
});
});
};
$scope.$watch(
function getLocationPath() { return $location.path(); },
function handleLocationPathChange(path) { that.updateCurrentPage(path); }
);
}]);

15
docs/app/js/code.js Normal file
View File

@ -0,0 +1,15 @@
angular.module('code', [])
.directive('code', function() {
return {
restrict: 'E',
terminal: true,
compile: function(element) {
var linenums = element.hasClass('linenum');
var match = /lang-(\S+)/.exec(element[0].className);
var lang = match && match[1];
var html = element.html();
element.html(window.prettyPrintOne(html, lang, linenums));
}
};
});

20
docs/bower.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "angular-docs",
"main": "index.js",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular",
"authors": [],
"license": "Apache-2.0",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"angular-material": "~0.6.0",
"google-code-prettify": "~1.0.3"
}
}

117
docs/docs-package/index.js Normal file
View File

@ -0,0 +1,117 @@
var Package = require('dgeni').Package;
var jsdocPackage = require('dgeni-packages/jsdoc');
var nunjucksPackage = require('dgeni-packages/nunjucks');
var typescriptPackage = require('../typescript-package');
var linksPackage = require('../links-package');
var gitPackage = require('dgeni-packages/git');
var path = require('canonical-path');
// Define the dgeni package for generating the docs
module.exports = new Package('angular-v2-docs', [jsdocPackage, nunjucksPackage, typescriptPackage, linksPackage, gitPackage])
// Register the services and file readers
.factory(require('./readers/ngdoc'))
// Register the processors
.processor(require('./processors/convertPrivateClassesToInterfaces'))
.processor(require('./processors/generateNavigationDoc'))
.processor(require('./processors/extractTitleFromGuides'))
.processor(require('./processors/createOverviewDump'))
.processor(require('./processors/checkUnbalancedBackTicks'))
// Configure the log service
.config(function(log) {
log.level = 'warn';
})
.config(function(renderDocsProcessor, versionInfo) {
renderDocsProcessor.extraData.versionInfo = versionInfo;
})
// Configure file reading
.config(function(readFilesProcessor, ngdocFileReader, readTypeScriptModules) {
readFilesProcessor.fileReaders = [ngdocFileReader];
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
readFilesProcessor.sourceFiles = [
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
];
readTypeScriptModules.sourceFiles = [
'*/*.@(js|es6|ts)',
'*/src/**/*.@(js|es6|ts)'
];
readTypeScriptModules.basePath = path.resolve(readFilesProcessor.basePath, 'modules');
})
.config(function(parseTagsProcessor, getInjectables) {
// We actually don't want to parse param docs in this package as we are getting the data out using TS
parseTagsProcessor.tagDefinitions.forEach(function(tagDef) {
if (tagDef.name === 'param') {
tagDef.docProperty = 'paramData';
tagDef.transforms = [];
}
});
})
// Configure links
.config(function(getLinkInfo) {
getLinkInfo.useFirstAmbiguousLink = true;
})
// Configure file writing
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/docs';
})
// Configure rendering
.config(function(templateFinder, templateEngine) {
// Nunjucks and Angular conflict in their template bindings so change Nunjucks
templateEngine.config.tags = {
variableStart: '{$',
variableEnd: '$}'
};
templateFinder.templateFolders
.unshift(path.resolve(__dirname, 'templates'));
templateFinder.templatePatterns = [
'${ doc.template }',
'${ doc.id }.${ doc.docType }.template.html',
'${ doc.id }.template.html',
'${ doc.docType }.template.html',
'common.template.html'
];
})
// Configure ids and paths
.config(function(computeIdsProcessor, computePathsProcessor) {
computeIdsProcessor.idTemplates.push({
docTypes: ['guide'],
getId: function(doc) {
return doc.fileInfo.relativePath
// path should be relative to `modules` folder
.replace(/.*\/?modules\//, '')
// path should not include `/docs/`
.replace(/\/docs\//, '/')
// path should not have a suffix
.replace(/\.\w*$/, '');
},
getAliases: function(doc) { return [doc.id]; }
});
computePathsProcessor.pathTemplates.push({
docTypes: ['guide'],
pathTemplate: '/${id}',
outputPathTemplate: 'partials/guides/${id}.html'
});
});

View File

@ -0,0 +1 @@
export var x = 100;

View File

@ -0,0 +1,10 @@
var Package = require('dgeni').Package;
module.exports = function mockPackage() {
return new Package('mockPackage', [require('../')])
// provide a mock log service
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); });
};

View File

@ -0,0 +1,34 @@
/**
* @module
* @description
* This is the module description
*/
export * from 'importedSrc';
/**
* This is some random other comment
*/
/**
* This is MyClass
*/
export class MyClass {
message: String;
/**
* Create a new MyClass
* @param {String} name The name to say hello to
*/
constructor(name) { this.message = 'hello ' + name; }
/**
* Return a greeting message
*/
greet() { return this.message; }
}
/**
* An exported function
*/
export var myFn = (val: number) => return val * 2;

View File

@ -0,0 +1,28 @@
var _ = require('lodash');
/**
* @dgProcessor checkUnbalancedBackTicks
* @description
* Searches the rendered content for an odd number of (```) backticks,
* which would indicate an unbalanced pair and potentially a typo in the
* source content.
*/
module.exports = function checkUnbalancedBackTicks(log, createDocMessage) {
var BACKTICK_REGEX = /^ *```/gm;
return {
$runAfter: ['checkAnchorLinksProcessor'],
$process: function(docs) {
_.forEach(docs, function(doc) {
if ( doc.renderedContent ) {
var matches = doc.renderedContent.match(BACKTICK_REGEX);
if (matches && matches.length % 2 !== 0) {
log.warn(createDocMessage('checkUnbalancedBackTicks processor: unbalanced backticks found in rendered content', doc));
console.log(doc.renderedContent);
}
}
});
}
};
};

View File

@ -0,0 +1,10 @@
module.exports = function convertPrivateClassesToInterfacesProcessor(convertPrivateClassesToInterfaces) {
return {
$runAfter: ['processing-docs'],
$runBefore: ['docs-processed'],
$process: function(docs) {
convertPrivateClassesToInterfaces(docs, false);
return docs;
}
};
};

View File

@ -0,0 +1,24 @@
var _ = require('lodash');
module.exports = function createOverviewDump() {
return {
$runAfter: ['processing-docs'],
$runBefore: ['docs-processed'],
$process: function(docs) {
var overviewDoc = {
id: 'overview-dump',
aliases: ['overview-dump'],
path: 'overview-dump',
outputPath: 'overview-dump.html',
modules: []
};
_.forEach(docs, function(doc) {
if ( doc.docType === 'module' ) {
overviewDoc.modules.push(doc);
}
});
docs.push(overviewDoc);
}
};
};

View File

@ -0,0 +1,24 @@
var _ = require('lodash');
module.exports = function extractTitleFromGuides() {
return {
$runAfter: ['processing-docs'],
$runBefore: ['docs-processed'],
$process: function(docs) {
_(docs).forEach(function(doc) {
if (doc.docType === 'guide') {
doc.name = doc.name || getNameFromHeading(doc.description);
}
});
}
};
};
function getNameFromHeading(text) {
var match = /^\s*#\s*(.*)/.exec(text);
if (match) {
return match[1];
}
}

View File

@ -0,0 +1,68 @@
var _ = require('lodash');
module.exports = function generateNavigationDoc() {
return {
$runAfter: ['docs-processed'],
$runBefore: ['rendering-docs'],
$process: function(docs) {
var modulesDoc = {
value: { sections: [] },
moduleName: 'navigation-modules',
serviceName: 'MODULES',
template: 'data-module.template.js',
outputPath: 'js/navigation-modules.js'
};
_.forEach(docs, function(doc) {
if ( doc.docType === 'module' ) {
var moduleNavItem = {
path: doc.path,
partial: doc.outputPath,
name: doc.id,
type: 'module',
pages: []
};
modulesDoc.value.sections.push(moduleNavItem);
_.forEach(doc.exports, function(exportDoc) {
if (!exportDoc.private) {
var exportNavItem = {
path: exportDoc.path,
partial: exportDoc.outputPath,
name: exportDoc.name,
type: exportDoc.docType
};
moduleNavItem.pages.push(exportNavItem);
}
});
}
});
docs.push(modulesDoc);
var guidesDoc = {
value: { pages: [] },
moduleName: 'navigation-guides',
serviceName: 'GUIDES',
template: 'data-module.template.js',
outputPath: 'js/navigation-guides.js'
};
_.forEach(docs, function(doc) {
if ( doc.docType === 'guide' ) {
var guideDoc = {
path: doc.path,
partial: doc.outputPath,
name: doc.name,
type: 'guide'
};
guidesDoc.value.pages.push(guideDoc);
}
});
docs.push(guidesDoc);
}
};
};

View File

@ -0,0 +1,32 @@
var path = require('canonical-path');
/**
* @dgService ngdocFileReader
* @description
* This file reader will pull the contents from a text file (by default .ngdoc)
*
* The doc will initially have the form:
* ```
* {
* content: 'the content of the file',
* startingLine: 1
* }
* ```
*/
module.exports = function ngdocFileReader() {
var reader = {
name: 'ngdocFileReader',
defaultPattern: /\.md$/,
getDocs: function(fileInfo) {
// We return a single element array because ngdoc files only contain one document
return [{
docType: 'guide',
content: fileInfo.content,
startingLine: 1
}];
}
};
return reader;
};

View File

@ -0,0 +1,45 @@
var ngdocFileReaderFactory = require('./ngdoc');
var path = require('canonical-path');
describe('ngdocFileReader', function() {
var fileReader;
var createFileInfo = function(file, content, basePath) {
return {
fileReader: fileReader.name,
filePath: file,
baseName: path.basename(file, path.extname(file)),
extension: path.extname(file).replace(/^\./, ''),
basePath: basePath,
relativePath: path.relative(basePath, file),
content: content
};
};
beforeEach(function() {
fileReader = ngdocFileReaderFactory();
});
describe('defaultPattern', function() {
it('should match .md files', function() {
expect(fileReader.defaultPattern.test('abc.md')).toBeTruthy();
expect(fileReader.defaultPattern.test('abc.js')).toBeFalsy();
});
});
describe('getDocs', function() {
it('should return an object containing info about the file and its contents', function() {
var fileInfo = createFileInfo('project/path/modules/someModule/foo/docs/subfolder/bar.ngdoc', 'A load of content', 'project/path');
expect(fileReader.getDocs(fileInfo)).toEqual([{
docType: 'guide',
content: 'A load of content',
startingLine: 1
}]);
});
});
});

View File

@ -0,0 +1,44 @@
{% include "lib/paramList.html" -%}
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' -%}
{% block body %}
<h1 class="class export">{$ doc.name $} <span class="type">{$ doc.docType $}</span></h1>
<p class="module">exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }<br/>
defined in {$ githubViewLink(doc) $}
</p>
<p>{$ doc.description | marked $}</p>
{%- if doc.decorators %}
<h2>Annotations</h2>
{%- for decorator in doc.decorators %}
<h3 class="annotation">@{$ decorator.name $}{$ paramList(decorator.arguments) $}</h3>
{% endfor %}
{% endif -%}
{%- if doc.constructorDoc or doc.members.length -%}
<h2>Members</h2>
{%- if doc.constructorDoc %}
<section class="member constructor">
<h1 id="constructor" class="name">{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) $}</h1>
{% marked %}
{$ doc.constructorDoc.description $}
{% endmarked %}
</section>
{% endif -%}
{%- for member in doc.members %}{% if not member.private %}
<section class="member">
<h1 id="{$ member.name $}" class="name">
{$ member.name $}{% if member.optional %}?{% endif %}{$ paramList(member.params) $}
</h1>
{% marked %}
{$ member.description $}
{% endmarked %}
</section>
{% endif %}{% endfor %}
{%- endif -%}
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends 'layout/base.template.html' %}
{% block body %}
<h1>{$ doc.id $}</h1>
<h2>({$ doc.docType $})</h2>
<div>
{$ doc.description | marked $}
</div>
{% endblock %}

View File

@ -0,0 +1 @@
{% extends 'var.template.html' -%}

View File

@ -0,0 +1,3 @@
angular.module('{$ doc.moduleName $}', [])
.value('{$ doc.serviceName $}', {$ doc.value | json $});

View File

@ -0,0 +1,11 @@
{% include "lib/paramList.html" -%}
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' -%}
{% block body %}
<h1 class="function export">{$ doc.name $}{$ paramList(doc.parameters) $}</h1>
<p class="module">exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }<br/>
defined in {$ githubViewLink(doc) $}</p>
<p>{$ doc.description | marked $}</p>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends 'layout/base.template.html' %}
{% block body %}
{$ doc.description | marked $}
{% endblock %}

View File

@ -0,0 +1 @@
{% extends 'class.template.html' -%}

View File

@ -0,0 +1 @@
{% block body %}{% endblock %}

View File

@ -0,0 +1,3 @@
{% macro githubViewLink(doc) -%}
<a href="https://github.com/{$ versionInfo.gitRepoInfo.owner $}/{$ versionInfo.gitRepoInfo.repo $}/tree/{$ versionInfo.currentVersion.isSnapshot and versionInfo.currentVersion.SHA or versionInfo.currentVersion.raw $}/modules/{$ doc.fileInfo.relativePath $}#L{$ doc.location.start.line+1 $}-L{$ doc.location.end.line+1 $}">{$ doc.fileInfo.relativePath $} (line {$ doc.location.start.line+1 $})</a>
{%- endmacro -%}

View File

@ -0,0 +1,7 @@
{% macro paramList(params) -%}
{%- if params -%}<span class="params">(
{%- for param in params -%}
<span class="param">{$ param | escape $}{% if not loop.last %}, {% endif %}</span>
{%- endfor %})</span>
{%- endif %}
{%- endmacro -%}

View File

@ -0,0 +1,19 @@
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' %}
{% block body %}
<h1 class="id">{$ doc.id $} <span class="type">module</span></h1>
<p>defined in {$ githubViewLink(doc) $}</p>
<p>{$ doc.description | marked $}</p>
{% if doc.exports.length %}
<h2>Exports</h2>
<ul>
{%- for exportDoc in doc.exports %}
{% if not exportDoc.private -%}
<li><a href="{$ exportDoc.path $}"><strong>{$ exportDoc.name $}</strong> {$ exportDoc.docType $}</a></li>
{%- endif %}
{%- endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,43 @@
{% include "lib/paramList.html" -%}
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
h2 {
padding-left: 20px;
}
h3 {
padding-left: 50px;
}
h4 {
padding-left: 60px;
}
</style>
</head>
<body>
<h1>Modules</h1>
{% for module in doc.modules %}
<h2>{$ module.id $}
{%- if module.public %} (public){% endif %}</h2>
{% for export in module.exports %}
<h3>{$ export.name $}</h3>
{%- if export.constructorDoc %}
<h4>{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) $}</h4>
{% endif -%}
{%- for member in export.members %}
<h4>{$ member.name $}{$ paramList(member.params) $}</h4>
{% endfor %}
{% endfor %}
{% endfor %}
</body>
</html>

View File

@ -0,0 +1,10 @@
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' %}
{% block body %}
<h1>{$ doc.name $} <span class="type">type alias</span></h1>
<p class="module">exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }<br/>
defined in {$ githubViewLink(doc) $}</p>
<p>{$ doc.description | marked $}</p>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% include "lib/githubLinks.html" -%}
{% extends 'layout/base.template.html' %}
{% block body %}
<h1>{$ doc.name $} <span class="type">variable</span></h1>
<p class="module">exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }<br/>
defined in {$ githubViewLink(doc) $}</p>
<p>{$ doc.description | marked $}</p>
{% endblock %}

View File

@ -0,0 +1,12 @@
var Package = require('dgeni').Package;
module.exports = new Package('links', [])
.factory(require('./inline-tag-defs/link'))
.factory(require('dgeni-packages/ngdoc/services/getAliases'))
.factory(require('dgeni-packages/ngdoc/services/getDocFromAlias'))
.factory(require('./services/getLinkInfo'))
.config(function(inlineTagProcessor, linkInlineTagDef) {
inlineTagProcessor.inlineTagDefinitions.push(linkInlineTagDef);
});

View File

@ -0,0 +1,33 @@
var INLINE_LINK = /(\S+)(?:\s+([\s\S]+))?/;
/**
* @dgService linkInlineTagDef
* @description
* Process inline link tags (of the form {@link some/uri Some Title}), replacing them with HTML anchors
* @kind function
* @param {Object} url The url to match
* @param {Function} docs error message
* @return {String} The html link information
*
* @property {boolean} relativeLinks Whether we expect the links to be relative to the originating doc
*/
module.exports = function linkInlineTagDef(getLinkInfo, createDocMessage, log) {
return {
name: 'link',
description: 'Process inline link tags (of the form {@link some/uri Some Title}), replacing them with HTML anchors',
handler: function(doc, tagName, tagDescription) {
// Parse out the uri and title
return tagDescription.replace(INLINE_LINK, function(match, uri, title) {
var linkInfo = getLinkInfo(uri, title, doc);
if ( !linkInfo.valid ) {
log.warn(createDocMessage(linkInfo.error, doc));
}
return "<a href='" + linkInfo.url + "'>" + linkInfo.title + "</a>";
});
}
};
};

View File

@ -0,0 +1,72 @@
var _ = require('lodash');
var path = require('canonical-path');
/**
* @dgService getLinkInfo
* @description
* Get link information to a document that matches the given url
* @kind function
* @param {String} url The url to match
* @param {String} title An optional title to return in the link information
* @return {Object} The link information
*
* @property {boolean} relativeLinks Whether we expect the links to be relative to the originating doc
*/
module.exports = function getLinkInfo(getDocFromAlias, encodeCodeBlock, log) {
return function getLinkInfoImpl(url, title, currentDoc) {
var linkInfo = {
url: url,
type: 'url',
valid: true,
title: title || url
};
if ( !url ) {
throw new Error('Invalid url');
}
var docs = getDocFromAlias(url, currentDoc);
if ( !getLinkInfoImpl.useFirstAmbiguousLink && docs.length > 1 ) {
linkInfo.valid = false;
linkInfo.errorType = 'ambiguous';
linkInfo.error = 'Ambiguous link: "' + url + '".\n' +
docs.reduce(function(msg, doc) { return msg + '\n "' + doc.id + '" ('+ doc.docType + ') : (' + doc.path + ' / ' + doc.fileInfo.relativePath + ')'; }, 'Matching docs: ');
} else if ( docs.length >= 1 ) {
linkInfo.url = docs[0].path;
linkInfo.title = title || encodeCodeBlock(docs[0].name, true);
linkInfo.type = 'doc';
if ( getLinkInfoImpl.relativeLinks && currentDoc && currentDoc.path ) {
var currentFolder = path.dirname(currentDoc.path);
var docFolder = path.dirname(linkInfo.url);
var relativeFolder = path.relative(path.join('/', currentFolder), path.join('/', docFolder));
linkInfo.url = path.join(relativeFolder, path.basename(linkInfo.url));
log.debug(currentDoc.path, docs[0].path, linkInfo.url);
}
} else if ( url.indexOf('#') > 0 ) {
var pathAndHash = url.split('#');
linkInfo = getLinkInfoImpl(pathAndHash[0], title, currentDoc);
linkInfo.url = linkInfo.url + '#' + pathAndHash[1];
return linkInfo;
} else if ( url.indexOf('/') === -1 && url.indexOf('#') !== 0 ) {
linkInfo.valid = false;
linkInfo.errorType = 'missing';
linkInfo.error = 'Invalid link (does not match any doc): "' + url + '"';
} else {
linkInfo.title = title || (( url.indexOf('#') === 0 ) ? url.substring(1) : path.basename(url, '.html'));
}
return linkInfo;
};
};

View File

@ -0,0 +1,24 @@
var Package = require('dgeni').Package;
var basePackage = require('../docs-package');
module.exports = new Package('angular-v2-public-docs', [basePackage])
.config(function(readTypeScriptModules) {
readTypeScriptModules.sourceFiles = [
'angular2/lifecycle_hooks.ts',
'angular2/core.ts',
'angular2/http.ts',
'angular2/router.ts',
'angular2/test.ts'
];
readTypeScriptModules.hidePrivateMembers = true;
})
.config(function(getLinkInfo) {
getLinkInfo.useFirstAmbiguousLink = false;
})
// Configure file writing
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/public_docs';
});

View File

@ -0,0 +1,121 @@
var Package = require('dgeni').Package;
var jsdocPackage = require('dgeni-packages/jsdoc');
var nunjucksPackage = require('dgeni-packages/nunjucks');
var typescriptPackage = require('../typescript-package');
var gitPackage = require('dgeni-packages/git');
var path = require('canonical-path');
// Define the dgeni package for generating the docs
module.exports = new Package('angular-v2-docs', [jsdocPackage, nunjucksPackage, typescriptPackage, gitPackage])
// Register the processors
.processor(require('./processors/createTypeDefinitionFile'))
.config(function(readFilesProcessor, inlineTagProcessor) {
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
// Don't run unwanted processors
readFilesProcessor.$enabled = false;
inlineTagProcessor.$enabled = false;
})
// Configure the log service
.config(function(log) {
log.level = 'info';
})
.config(function(renderDocsProcessor, versionInfo) {
renderDocsProcessor.extraData.versionInfo = versionInfo;
})
.config(function(readFilesProcessor, inlineTagProcessor, readTypeScriptModules, createTypeDefinitionFile) {
// Don't run unwanted processors
readFilesProcessor.$enabled = false; // We are not using the normal file reading processor
inlineTagProcessor.$enabled = false; // We are not actually processing the inline link tags
// Configure file reading
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
readTypeScriptModules.sourceFiles = [
'angular2/angular2.ts',
'angular2/web_worker/worker.ts',
'angular2/web_worker/ui.ts',
'angular2/router.ts',
'angular2/http.ts',
'angular2/test_lib.ts'
];
readTypeScriptModules.basePath = path.resolve(path.resolve(__dirname, '../../modules'));
createTypeDefinitionFile.typeDefinitions = [
{
id: 'angular2/angular2',
references: ['../es6-shim/es6-shim.d.ts'],
modules: {
'angular2/angular2': {namespace: 'ng', id: 'angular2/angular2'},
'angular2/web_worker/worker': {namespace: 'ngWorker', id: 'angular2/web_worker/worker'},
'angular2/web_worker/ui': {namespace: 'ngUi', id: 'angular2/web_worker/ui'}
}
},
{
id: 'angular2/router',
references: ['./angular2.d.ts'],
remapTypes: {Type: 'ng.Type', InjectableReference: 'ng.InjectableReference'},
modules: {'angular2/router': {namespace: 'ngRouter', id: 'angular2/router'}}
},
{
id: 'angular2/http',
references: ['./angular2.d.ts'],
remapTypes: {Type: 'ng.Type', Observable: 'ng.Observable', EventEmitter: 'ng.EventEmitter', InjectableReference: 'ng.InjectableReference' },
modules: {'angular2/http': {namespace: 'ngHttp', id: 'angular2/http'}}
},
{
id: 'angular2/test_lib',
references: ['./angular2.d.ts', '../jasmine/jasmine.d.ts'],
remapTypes: { Type: 'ng.Type', Binding: 'ng.Binding', ViewMetadata: 'ng.ViewMetadata', Injector: 'ng.Injector',
Predicate: 'ng.Predicate', ElementRef: 'ng.ElementRef', DebugElement: 'ng.DebugElement',
InjectableReference: 'ng.InjectableReference' },
modules: {'angular2/test_lib': {namespace: 'ngTestLib', id: 'angular2/test_lib'}}
}
];
})
.config(function(parseTagsProcessor, getInjectables) {
// We actually don't want to parse param docs in this package as we are getting the data out using TS
parseTagsProcessor.tagDefinitions.forEach(function(tagDef) {
if (tagDef.name === 'param') {
tagDef.docProperty = 'paramData';
tagDef.transforms = [];
}
});
})
// Configure file writing
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/docs';
})
// Configure rendering
.config(function(templateFinder, templateEngine) {
// Nunjucks and Angular conflict in their template bindings so change Nunjucks
templateEngine.config.tags = {
variableStart: '{$',
variableEnd: '$}'
};
templateFinder.templateFolders
.unshift(path.resolve(__dirname, 'templates'));
templateFinder.templatePatterns = [
'${ doc.template }',
'${ doc.id }.${ doc.docType }.template.html',
'${ doc.id }.template.html',
'${ doc.docType }.template.html',
'common.template.html'
];
});

View File

@ -0,0 +1,11 @@
var Package = require('dgeni').Package;
module.exports = function mockPackage() {
return new Package('mockPackage', [require('../')])
// provide a mock log service
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
// .factory('templateEngine', function() { return {}; });
};

View File

@ -0,0 +1,216 @@
'use strict';
function DtsSerializer(remap) {
this.remap = remap;
}
DtsSerializer.prototype = {
_initializerRegex: /\s*=[^>][^,}]*/g,
constructor: DtsSerializer,
declaration: function(buffer, ast) {
buffer.push(ast.name);
if (ast.optional) buffer.push('?');
if (ast.typeParameters) {
buffer.push('<');
buffer.push(ast.typeParameters.join(', '));
buffer.push('>');
}
if (ast.parameters) {
buffer.push('(');
var parameters = ast.parameters;
for (var i = 0; i < parameters.length; i++) {
parameters[i] = parameters[i].replace(this._initializerRegex, '');
}
buffer.push(parameters.join(', '));
buffer.push(')');
}
if (!isConstructor(ast)) {
if (ast.returnType) {
buffer.push(': ', ast.returnType);
} else if (ast.parameters) {
buffer.push(': void');
} else {
buffer.push(': any');
}
}
buffer.push(';\n');
},
comment: function(buffer, commentText) {
if (!(commentText && commentText.match(/\S/))) return;
buffer.push('/**\n');
commentText.replace(/\n*$/, '').split('\n').forEach(function(line) {
buffer.push(' * ' + line + '\n');
});
buffer.push(' */\n');
},
member: function(buffer, ast) {
if (ast.private) return;
buffer.push('\n');
this.comment(buffer, ast.content);
if (ast.isStatic) buffer.push('static ');
this.declaration(buffer, ast);
},
interfaceOrClass: function(buffer, ast, isInterface) {
buffer.push(isInterface ? 'interface ' : 'class ');
buffer.push(ast.name);
buffer.push(ast.typeParams);
buffer.push(ast.heritage);
buffer.push(' {');
buffer.indent();
if (ast.newMember) this.member(buffer, ast.newMember);
if (ast.callMember) this.member(buffer, ast.callMember);
if (ast.constructorDoc) this.member(buffer, ast.constructorDoc);
ast.statics.forEach(function(staticMember) {
this.member(buffer, staticMember);
}.bind(this));
ast.members.forEach(function(member) {
this.member(buffer, member);
}.bind(this));
buffer.unindent();
buffer.push('}');
},
enum: function(buffer, ast) {
buffer.push('enum ');
buffer.push(ast.name);
buffer.push(ast.typeParams);
buffer.push(ast.heritage);
buffer.push(' {');
buffer.indent();
ast.members.forEach(function(member, index) {
buffer.push('\n');
this.comment(buffer, member.content);
buffer.push(member.name);
if (index !== (ast.members.length - 1)) {
buffer.push(',\n');
}
}.bind(this));
buffer.unindent();
buffer.push('}\n');
},
function: function(buffer, ast) {
buffer.push('function ');
this.declaration(buffer, ast);
},
var: function(buffer, ast) {
buffer.push('var ');
this.declaration(buffer, ast);
},
let: function(buffer, ast) {
buffer.push('let ');
this.declaration(buffer, ast);
},
const: function(buffer, ast) {
buffer.push('const ');
this.declaration(buffer, ast);
},
typeAlias: function(buffer, ast) {
buffer.push('type ', ast.name, ' = ', ast.returnType);
},
serializeExport: function(ast) {
var buffer = new Buffer();
buffer.push('\n');
try {
this.comment(buffer, ast.content);
switch (ast.docType) {
case 'class': this.interfaceOrClass(buffer, ast, false); break;
case 'interface': this.interfaceOrClass(buffer, ast, true); break;
case 'function': this.function(buffer, ast); break;
case 'enum': this.enum(buffer, ast); break;
case 'var': this.var(buffer, ast); break;
case 'let': this.let(buffer, ast); break;
case 'const': this.const(buffer, ast); break;
case 'type-alias': this.typeAlias(buffer, ast); break;
default: throw new Error("unknown docType: " + ast.docType);
}
var string = buffer.toString();
for (var key in this.remap) {
if (this.remap.hasOwnProperty(key)) {
string = string.replace(new RegExp('\\b' + key + '\\b', 'gm'), this.remap[key]);
}
}
return string;
} catch (e) {
console.log(e.toString(), e.stack);
return 'ERROR: ' + e.toString();
}
}
};
function Buffer() {
this._globalBuffer = [];
this._indentedBuffer = [];
this._indentationLevel = 1;
}
Buffer.prototype = {
constructor: Buffer,
push: function() {
this._indentedBuffer.push.apply(this._indentedBuffer, arguments);
},
indent: function() {
this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')});
this._indentationLevel++;
this._indentedBuffer = [];
},
unindent: function() {
this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')});
this._indentationLevel--;
this._indentedBuffer = [];
},
toString: function() {
if (this._indentationLevel !== 1) {
throw new Exception("Forgot to unindent? Indentation level: " + this._indentationLevel);
}
this.unindent();
var string = '';
this._globalBuffer.forEach(function(indentedChunk) {
var indentation = (new Array(indentedChunk.indentationLevel * 2 + 1)).join(' ');
indentedChunk.content.split('\n').forEach(function(line) {
string += indentation + line + '\n';
});
});
return string;
}
};
function isConstructor(ast) {
return ast.parameters && ast.name === "constructor";
}
module.exports = {
DtsSerializer: DtsSerializer
};

View File

@ -0,0 +1,72 @@
var _ = require('lodash');
var path = require('canonical-path');
var codeGen = require('./code_gen.js');
module.exports = function createTypeDefinitionFile(log, convertPrivateClassesToInterfaces) {
return {
$runAfter: ['processing-docs'],
$runBefore: ['docs-processed'],
$validate: {
dtsPath: { presence: true },
dtsExtension: { presence: true },
typeDefinitions: { presence: true }
},
dtsPath: 'typings',
dtsExtension: '.d.ts',
typeDefinitions: [],
$process: function(docs) {
var dtsPath = this.dtsPath;
var dtsExtension = this.dtsExtension;
// For each type definition that we wish to create we define a dgeni "doc" for it
var typeDefDocs = _.map(this.typeDefinitions, function(def) {
var id = def.id + dtsExtension;
var docPath = path.join(dtsPath, id);
return {
docType: 'type-definition',
id: id,
aliases: [id],
path: docPath,
outputPath: docPath,
// A type definition may include a number of top level modules
// And those modules could be aliased (such as 'angular2/angular2.api' ->
// 'angular2/angular2')
moduleDocs: _.transform(def.modules,
function(moduleDocs, props, alias) {
moduleDocs[props.id] = {
id: alias,
doc: null, namespace: props.namespace,
references: def.references
};
}),
dts: new codeGen.DtsSerializer(def.remapTypes)
};
});
// Now add all the module docs to their corresponding type definition doc
_.forEach(docs, function(doc) {
_.forEach(typeDefDocs, function(typeDefDoc) {
if(typeDefDoc.moduleDocs[doc.id]) {
// Add a copy, because we are going to modify it
typeDefDoc.moduleDocs[doc.id].doc = doc;
}
});
});
return _.filter(typeDefDocs, function(doc) {
_.forEach(doc.moduleDocs, function(modDoc, alias) {
if (!doc || !modDoc.doc) {
log.error('createTypeDefinitionFile processor: no such module "' + alias + '" (Did you forget to add it to the modules to load?)');
doc = null;
return;
}
convertPrivateClassesToInterfaces(modDoc.doc.exports, true);
});
return !!doc;
});
}
};
};

View File

@ -0,0 +1,53 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
var path = require('canonical-path');
var _ = require('lodash');
describe('createTypeDefinitionFile processor', function() {
var dgeni, injector, processor;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
processor = injector.get('createTypeDefinitionFile');
// Initialize the processor
processor.typeDefinitions = [{
id: 'angular2/angular2',
modules: {
'angular2/angular2': {
id: 'angular2/angular2',
namespace: 'ng'
}
}
}];
});
describe('classes with private constructors', function() {
it('should convert heritage from `implements` into `extends`', function() {
// Create some mock docs for testing
var docs = [
{
id: 'angular2/angular2',
exports: [
{ docType: 'class', heritage: 'implements Xyz', constructorDoc: { private: true } }
]
}
];
docs = processor.$process(docs);
expect(docs.length).toEqual(1);
expect(docs[0].docType).toEqual('type-definition');
var moduleDoc = docs[0].moduleDocs['angular2/angular2'].doc;
expect(moduleDoc.exports.length).toEqual(2);
expect(moduleDoc.exports[0].heritage).toEqual('extends Xyz');
});
});
});

View File

@ -0,0 +1,17 @@
{% extends '../type-definition.template.html' %}
{% block staticDeclarations %}
interface Map<K,V> {}
{% for alias, module in doc.moduleDocs %}
declare module {$ module.namespace $} {
// See https://github.com/Microsoft/TypeScript/issues/1168
class BaseException /* extends Error */ {
message: string;
stack: string;
toString(): string;
}
interface InjectableReference {}
}
{% endfor %}
{% endblock %}

View File

@ -0,0 +1,43 @@
{%- macro commentBlock(doc, level) -%}
{%- if doc.content | trim %}
{% if level > 1 %}{$ '/**' | indent(level-1, true) | replace(r/\n$/, "") $}{% else %}/**{% endif %}
{$ doc.content | trim | replace(r/^/gm, "* ") | indent(level, true) | replace(r/\n$/, "") $}
{$ '*/' | indent(level, true) | replace(r/\n$/, "") $}{% endif -%}
{%- endmacro -%}
// Type definitions for Angular v{$ versionInfo.currentVersion.full | replace(r/\+/, "_") $}
// Project: http://angular.io/
// Definitions by: angular team <https://github.com/angular/>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
// ***********************************************************
// This file is generated by the Angular build process.
// Please do not create manual edits or send pull requests
// modifying this file.
// ***********************************************************
{% for alias, module in doc.moduleDocs %}
{%- if module.references.length %}
// {$ alias $} depends transitively on these libraries.
// If you don't have them installed you can install them using TSD
// https://github.com/DefinitelyTyped/tsd
{%- endif %}
{% for reference in module.references %}
///<reference path="{$ reference $}"/>{% endfor %}{% endfor %}
{% block staticDeclarations %}{% endblock %}
{% for alias, module in doc.moduleDocs %}
{$ commentBlock(module.doc, 1) $}
declare module {$ module.namespace $} {
{%- for export in module.doc.exports -%}
{$ doc.dts.serializeExport(export) $}
{% endfor %}
}
declare module "{$ alias $}" {
export = {$ module.namespace $};
}
{% endfor %}

View File

@ -0,0 +1,70 @@
var basePackage = require('dgeni-packages/base');
var Package = require('dgeni').Package;
var path = require('canonical-path');
// Define the dgeni package for generating the docs
module.exports = new Package('typescript-parsing', [basePackage])
// Register the services and file readers
.factory(require('./services/modules'))
.factory(require('./services/tsParser'))
.factory(require('./services/tsParser/createCompilerHost'))
.factory(require('./services/tsParser/getFileInfo'))
.factory(require('./services/tsParser/getExportDocType'))
.factory(require('./services/tsParser/getContent'))
.factory(require('./services/convertPrivateClassesToInterfaces'))
.factory('EXPORT_DOC_TYPES', function() {
return [
'class',
'interface',
'function',
'var',
'const',
'enum',
'type-alias'
];
})
// Register the processors
.processor(require('./processors/readTypeScriptModules'))
// Configure the log service
.config(function(log) {
log.level = 'warn';
})
// Configure ids and paths
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
computeIdsProcessor.idTemplates.push({
docTypes: ['member'],
idTemplate: '${classDoc.id}.${name}',
getAliases: function(doc) { return [doc.id]; }
});
computePathsProcessor.pathTemplates.push({
docTypes: ['member'],
pathTemplate: '${classDoc.path}/${name}',
getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc
});
var MODULES_DOCS_PATH = 'partials/modules';
computePathsProcessor.pathTemplates.push({
docTypes: ['module'],
pathTemplate: '/${id}',
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.html'
});
computePathsProcessor.pathTemplates.push({
docTypes: EXPORT_DOC_TYPES,
pathTemplate: '${moduleDoc.path}/${name}',
outputPathTemplate: MODULES_DOCS_PATH + '/${path}/index.html'
});
});

View File

@ -0,0 +1,11 @@
var Package = require('dgeni').Package;
module.exports = function mockPackage() {
return new Package('mockPackage', [require('../')])
// provide a mock log service
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
.factory('templateEngine', function() { return {}; });
};

View File

@ -0,0 +1,4 @@
export var __esModule = true;
export class OKToExport {}
export function _thisIsPrivate() {}
export var thisIsOK = '!';

View File

@ -0,0 +1,5 @@
export interface MyInterface {
optionalProperty? : string
<T, U extends Findable<T>>(param: T) : U
new (param: number) : MyInterface
}

View File

@ -0,0 +1,6 @@
export class Test {
firstItem;
constructor() { this.doStuff(); }
otherMethod() {}
doStuff() {}
}

View File

@ -0,0 +1 @@
export var x = 10;

View File

@ -0,0 +1 @@
export { x as y} from './privateModule';

View File

@ -0,0 +1 @@
export var x = 100;

View File

@ -0,0 +1,34 @@
/**
* @module
* @description
* This is the module description
*/
export * from 'importedSrc';
/**
* This is some random other comment
*/
/**
* This is MyClass
*/
export class MyClass {
message: String;
/**
* Create a new MyClass
* @param {String} name The name to say hello to
*/
constructor(name) { this.message = 'hello ' + name; }
/**
* Return a greeting message
*/
greet() { return this.message; }
}
/**
* An exported function
*/
export var myFn = (val: number) => return val * 2;

View File

@ -0,0 +1,406 @@
var glob = require('glob');
var path = require('canonical-path');
var _ = require('lodash');
var ts = require('typescript');
module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
getExportDocType, getContent, log) {
return {
$runAfter: ['files-read'],
$runBefore: ['parsing-tags'],
$validate: {
sourceFiles: {presence: true},
basePath: {presence: true},
hidePrivateMembers: {inclusion: [true, false]},
sortClassMembers: {inclusion: [true, false]},
ignoreExportsMatching: {}
},
// A collection of globs that identify those modules for which we should create docs
sourceFiles: [],
// The base path from which to load the source files
basePath: '.',
// We can ignore members of classes that are private
hidePrivateMembers: true,
// We leave class members sorted in order of declaration
sortClassMembers: false,
// We can provide a collection of strings or regexes to ignore exports whose export names match
ignoreExportsMatching: ['___esModule'],
$process: function(docs) {
// Convert ignoreExportsMatching to an array of regexes
var ignoreExportsMatching = convertToRegexCollection(this.ignoreExportsMatching);
var hidePrivateMembers = this.hidePrivateMembers;
var sortClassMembers = this.sortClassMembers;
var basePath = path.resolve(this.basePath);
var filesPaths = expandSourceFiles(this.sourceFiles, basePath);
var parseInfo = tsParser.parse(filesPaths, this.basePath);
var moduleSymbols = parseInfo.moduleSymbols;
// Iterate through each of the modules that were parsed and generate a module doc
// as well as docs for each module's exports.
moduleSymbols.forEach(function(moduleSymbol) {
var moduleDoc = createModuleDoc(moduleSymbol, basePath);
// Add this module doc to the module lookup collection and the docs collection
modules[moduleDoc.id] = moduleDoc;
docs.push(moduleDoc);
// Iterate through this module's exports and generate a doc for each
moduleSymbol.exportArray.forEach(function(exportSymbol) {
// Ignore exports starting with an underscore
if (anyMatches(ignoreExportsMatching, exportSymbol.name)) return;
// If the symbol is an Alias then for most things we want the original resolved symbol
var resolvedExport = exportSymbol.resolvedSymbol || exportSymbol;
var exportDoc = createExportDoc(exportSymbol.name, resolvedExport, moduleDoc, basePath, parseInfo.typeChecker);
log.debug('>>>> EXPORT: ' + exportDoc.name + ' (' + exportDoc.docType + ') from ' + moduleDoc.id);
exportDoc.members = [];
exportDoc.statics = [];
// Generate docs for each of the export's members
if (resolvedExport.flags & ts.SymbolFlags.HasMembers) {
for(var memberName in resolvedExport.members) {
// FIXME(alexeagle): why do generic type params appear in members?
if (memberName === 'T') {
continue;
}
log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
var memberSymbol = resolvedExport.members[memberName];
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
// We special case the constructor and sort the other members alphabetically
if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
exportDoc.constructorDoc = memberDoc;
docs.push(memberDoc);
} else if (!hidePrivateMembers || memberSymbol.name.charAt(0) !== '_') {
docs.push(memberDoc);
exportDoc.members.push(memberDoc);
} else if (memberSymbol.name === '__call' && memberSymbol.flags & ts.SymbolFlags.Signature) {
docs.push(memberDoc);
exportDoc.callMember = memberDoc;
} else if (memberSymbol.name === '__new' && memberSymbol.flags & ts.SymbolFlags.Signature) {
docs.push(memberDoc);
exportDoc.newMember = memberDoc;
}
}
}
if (exportDoc.docType === 'enum') {
for(var memberName in resolvedExport.exports) {
log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
var memberSymbol = resolvedExport.exports[memberName];
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
docs.push(memberDoc);
exportDoc.members.push(memberDoc);
}
} else if (resolvedExport.flags & ts.SymbolFlags.HasExports) {
for (var exported in resolvedExport.exports) {
if (exported === 'prototype') continue;
if (hidePrivateMembers && exported.charAt(0) === '_') continue;
var memberSymbol = resolvedExport.exports[exported];
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
memberDoc.isStatic = true;
docs.push(memberDoc);
exportDoc.statics.push(memberDoc);
}
}
if (sortClassMembers) {
exportDoc.members.sort(function(a, b) {
if (a.name > b.name) return 1;
if (a.name < b.name) return -1;
return 0;
});
}
// Add this export doc to its module doc
moduleDoc.exports.push(exportDoc);
docs.push(exportDoc);
});
});
}
};
function createModuleDoc(moduleSymbol, basePath) {
var id = moduleSymbol.name.replace(/^"|"$/g, '');
var moduleDoc = {
docType: 'module',
id: id,
aliases: [id],
moduleTree: moduleSymbol,
content: getContent(moduleSymbol),
exports: [],
fileInfo: getFileInfo(moduleSymbol, basePath),
location: getLocation(moduleSymbol)
};
return moduleDoc;
}
function createExportDoc(name, exportSymbol, moduleDoc, basePath, typeChecker) {
var typeParamString = '';
var heritageString = '';
var typeDefinition = '';
exportSymbol.declarations.forEach(function(decl) {
var sourceFile = ts.getSourceFileOfNode(decl);
if (decl.typeParameters) {
typeParamString = '<' + getText(sourceFile, decl.typeParameters) + '>';
}
if (decl.symbol.flags & ts.SymbolFlags.TypeAlias) {
typeDefinition = getText(sourceFile, decl.type);
}
if (decl.heritageClauses) {
decl.heritageClauses.forEach(function(heritage) {
if (heritage.token == ts.SyntaxKind.ExtendsKeyword) {
heritageString += " extends";
heritage.types.forEach(function(typ, idx) {
heritageString += (idx > 0 ? ',' : '') + typ.getFullText();
});
}
if (heritage.token == ts.SyntaxKind.ImplementsKeyword) {
heritageString += " implements";
heritage.types.forEach(function(typ, idx) {
heritageString += (idx > 0 ? ', ' : '') + typ.getFullText();
});
}
});
}
});
//Make sure duplicate aliases aren't created, so "Ambiguous link" warnings are prevented
var aliasNames = [name, moduleDoc.id + '/' + name];
if (typeParamString) {
aliasNames.push(name + typeParamString);
aliasNames.push(moduleDoc.id + '/' + name + typeParamString);
}
var exportDoc = {
docType: getExportDocType(exportSymbol),
name: name,
id: moduleDoc.id + '/' + name,
typeParams: typeParamString,
heritage: heritageString,
decorators: getDecorators(exportSymbol),
aliases: aliasNames,
moduleDoc: moduleDoc,
content: getContent(exportSymbol),
fileInfo: getFileInfo(exportSymbol, basePath),
location: getLocation(exportSymbol)
};
if (exportDoc.docType === 'var' || exportDoc.docType === 'const') {
exportDoc.symbolTypeName = exportSymbol.valueDeclaration.type &&
exportSymbol.valueDeclaration.type.typeName &&
exportSymbol.valueDeclaration.type.typeName.text;
}
if (exportDoc.docType === 'type-alias') {
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
}
if(exportSymbol.flags & ts.SymbolFlags.Function) {
exportDoc.parameters = getParameters(typeChecker, exportSymbol);
}
if(exportSymbol.flags & ts.SymbolFlags.Value) {
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
}
if (exportSymbol.flags & ts.SymbolFlags.TypeAlias) {
exportDoc.typeDefinition = typeDefinition;
}
// Compute the original module name from the relative file path
exportDoc.originalModule = exportDoc.fileInfo.relativePath
.replace(new RegExp('\.' + exportDoc.fileInfo.extension + '$'), '');
return exportDoc;
}
function createMemberDoc(memberSymbol, classDoc, basePath, typeChecker) {
var memberDoc = {
docType: 'member',
classDoc: classDoc,
name: memberSymbol.name,
decorators: getDecorators(memberSymbol),
content: getContent(memberSymbol),
fileInfo: getFileInfo(memberSymbol, basePath),
location: getLocation(memberSymbol)
};
memberDoc.typeParameters = getTypeParameters(typeChecker, memberSymbol);
if(memberSymbol.flags & (ts.SymbolFlags.Signature) ) {
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
switch(memberDoc.name) {
case '__call':
memberDoc.name = '';
break;
case '__new':
memberDoc.name = 'new';
break;
}
}
if (memberSymbol.flags & ts.SymbolFlags.Method) {
// NOTE: we use the property name `parameters` here so we don't conflict
// with the `params` property that will be updated by dgeni reading the
// `@param` tags from the docs
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
}
if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
memberDoc.name = 'constructor';
}
if(memberSymbol.flags & ts.SymbolFlags.Value) {
memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
}
if(memberSymbol.flags & ts.SymbolFlags.Optional) {
memberDoc.optional = true;
}
return memberDoc;
}
function getDecorators(symbol) {
var declaration = symbol.valueDeclaration || symbol.declarations[0];
var sourceFile = ts.getSourceFileOfNode(declaration);
var decorators = declaration.decorators && declaration.decorators.map(function(decorator) {
decorator = decorator.expression;
return {
name: decorator.expression ? decorator.expression.text : decorator.text,
arguments: decorator.arguments && decorator.arguments.map(function(argument) {
return getText(sourceFile, argument).trim();
})
};
});
return decorators;
}
function getParameters(typeChecker, symbol) {
var declaration = symbol.valueDeclaration || symbol.declarations[0];
var sourceFile = ts.getSourceFileOfNode(declaration);
if (!declaration.parameters) {
var location = getLocation(symbol);
throw new Error('missing declaration parameters for "' + symbol.name +
'" in ' + sourceFile.fileName +
' at line ' + location.start.line);
}
return declaration.parameters.map(function(parameter) {
var paramText = '';
if (parameter.dotDotDotToken) {
paramText += '...';
}
paramText += getText(sourceFile, parameter.name);
if (parameter.questionToken || parameter.initializer) {
paramText += '?';
}
if (parameter.type) {
paramText += ':' + getType(sourceFile, parameter.type);
} else {
paramText += ': any';
if (parameter.dotDotDotToken) {
paramText += '[]';
}
}
return paramText.trim();
});
}
function getTypeParameters(typeChecker, symbol) {
var declaration = symbol.valueDeclaration || symbol.declarations[0];
var sourceFile = ts.getSourceFileOfNode(declaration);
if (!declaration.typeParameters) return;
var typeParams = declaration.typeParameters.map(function(type) {
return getText(sourceFile, type).trim();
});
return typeParams;
}
function getReturnType(typeChecker, symbol) {
var declaration = symbol.valueDeclaration || symbol.declarations[0];
var sourceFile = ts.getSourceFileOfNode(declaration);
if (declaration.type) {
return getType(sourceFile, declaration.type).trim();
}
}
function expandSourceFiles(sourceFiles, basePath) {
var filePaths = [];
sourceFiles.forEach(function(sourcePattern) {
filePaths = filePaths.concat(glob.sync(sourcePattern, { cwd: basePath }));
});
return filePaths;
}
function getText(sourceFile, node) {
return sourceFile.text.substring(node.pos, node.end);
}
// Strip any local renamed imports from the front of types
function getType(sourceFile, type) {
var text = getText(sourceFile, type);
while (text.indexOf(".") >= 0) {
// Keep namespaced symbols in RxNext
if (text.match(/^\s*RxNext\./)) break;
// handle the case List<thing.stuff> -> List<stuff>
text = text.replace(/([^.<]*)\.([^>]*)/, "$2");
}
return text;
}
function getLocation(symbol) {
var node = symbol.valueDeclaration || symbol.declarations[0];
var sourceFile = ts.getSourceFileOfNode(node);
var location = {
start: ts.getLineAndCharacterOfPosition(sourceFile, node.pos),
end: ts.getLineAndCharacterOfPosition(sourceFile, node.end)
};
return location;
}
};
function convertToRegexCollection(items) {
if (!items) return [];
// Must be an array
if (!_.isArray(items)) {
items = [items];
}
// Convert string to exact matching regexes
return items.map(function(item) {
return _.isString(item) ? new RegExp('^' + item + '$') : item;
});
}
function anyMatches(regexes, item) {
for(var i=0; i<regexes.length; ++i) {
if ( item.match(regexes[i]) ) return true;
}
return false;
}

View File

@ -0,0 +1,127 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
var path = require('canonical-path');
var _ = require('lodash');
describe('readTypeScriptModules', function() {
var dgeni, injector, processor;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
processor = injector.get('readTypeScriptModules');
processor.basePath = path.resolve(__dirname, '../mocks/readTypeScriptModules');
});
describe('exportDocs', function() {
it('should provide the original module if the export is re-exported', function() {
processor.sourceFiles = [ 'publicModule.ts' ];
var docs = [];
processor.$process(docs);
var exportedDoc = docs[1];
expect(exportedDoc.originalModule).toEqual('privateModule');
});
});
describe('ignoreExportsMatching', function() {
it('should ignore exports that match items in the `ignoreExportsMatching` property', function() {
processor.sourceFiles = [ 'ignoreExportsMatching.ts'];
processor.ignoreExportsMatching = [/^_/];
var docs = [];
processor.$process(docs);
var moduleDoc = docs[0];
expect(moduleDoc.docType).toEqual('module');
expect(moduleDoc.exports).toEqual([
jasmine.objectContaining({ name: 'OKToExport' }),
jasmine.objectContaining({ name: 'thisIsOK' })
]);
});
it('should only ignore `___esModule` exports by default', function() {
processor.sourceFiles = [ 'ignoreExportsMatching.ts'];
var docs = [];
processor.$process(docs);
var moduleDoc = docs[0];
expect(moduleDoc.docType).toEqual('module');
expect(getNames(moduleDoc.exports)).toEqual([
'OKToExport',
'_thisIsPrivate',
'thisIsOK'
]);
});
});
describe('interfaces', function() {
it('should mark optional properties', function() {
processor.sourceFiles = [ 'interfaces.ts'];
var docs = [];
processor.$process(docs);
var moduleDoc = docs[0];
var exportedInterface = moduleDoc.exports[0];
var member = exportedInterface.members[0];
expect(member.name).toEqual('optionalProperty');
expect(member.optional).toEqual(true);
});
it('should handle "call" type interfaces', function() {
processor.sourceFiles = [ 'interfaces.ts'];
var docs = [];
processor.$process(docs);
var moduleDoc = docs[0];
var exportedInterface = moduleDoc.exports[0];
expect(exportedInterface.callMember).toBeDefined();
expect(exportedInterface.callMember.parameters).toEqual(['param: T']);
expect(exportedInterface.callMember.returnType).toEqual('U');
expect(exportedInterface.callMember.typeParameters).toEqual(['T', 'U extends Findable<T>']);
expect(exportedInterface.newMember).toBeDefined();
expect(exportedInterface.newMember.parameters).toEqual(['param: number']);
expect(exportedInterface.newMember.returnType).toEqual('MyInterface');
});
});
describe('ordering of members', function() {
it('should order class members in order of appearance (by default)', function() {
processor.sourceFiles = ['orderingOfMembers.ts'];
var docs = [];
processor.$process(docs);
var classDoc = _.find(docs, { docType: 'class' });
expect(classDoc.docType).toEqual('class');
expect(getNames(classDoc.members)).toEqual([
'firstItem',
'otherMethod',
'doStuff',
]);
});
it('should not order class members if not sortClassMembers is false', function() {
processor.sourceFiles = ['orderingOfMembers.ts'];
processor.sortClassMembers = false;
var docs = [];
processor.$process(docs);
var classDoc = _.find(docs, { docType: 'class' });
expect(classDoc.docType).toEqual('class');
expect(getNames(classDoc.members)).toEqual([
'firstItem',
'otherMethod',
'doStuff'
]);
});
});
});
function getNames(collection) {
return collection.map(function(item) { return item.name; });
}

View File

@ -0,0 +1,31 @@
var _ = require('lodash');
module.exports = function convertPrivateClassesToInterfaces() {
return function(exportDocs, addInjectableReference) {
_.forEach(exportDocs, function(exportDoc) {
// Search for classes with a constructor marked as `@private`
if (exportDoc.docType === 'class' && exportDoc.constructorDoc && exportDoc.constructorDoc.private) {
// Convert this class to an interface with no constructor
exportDoc.docType = 'interface';
exportDoc.constructorDoc = null;
if (exportDoc.heritage) {
// convert the heritage since interfaces use `extends` not `implements`
exportDoc.heritage = exportDoc.heritage.replace('implements', 'extends');
}
if (addInjectableReference) {
// Add the `declare var SomeClass extends InjectableReference` construct
exportDocs.push({
docType: 'var',
name: exportDoc.name,
id: exportDoc.id,
returnType: 'InjectableReference'
});
}
}
});
};
};

View File

@ -0,0 +1,76 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
var _ = require('lodash');
describe('readTypeScriptModules', function() {
var dgeni, injector, convertPrivateClassesToInterfaces;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
convertPrivateClassesToInterfaces = injector.get('convertPrivateClassesToInterfaces');
});
it('should convert private class docs to interface docs', function() {
var docs = [
{
docType: 'class',
name: 'privateClass',
id: 'privateClass',
constructorDoc: { private: true }
}
];
convertPrivateClassesToInterfaces(docs, false);
expect(docs[0].docType).toEqual('interface');
});
it('should not touch non-private class docs', function() {
var docs = [
{
docType: 'class',
name: 'privateClass',
id: 'privateClass',
constructorDoc: { }
}
];
convertPrivateClassesToInterfaces(docs, false);
expect(docs[0].docType).toEqual('class');
});
it('should convert the heritage since interfaces use `extends` not `implements`', function() {
var docs = [
{
docType: 'class',
name: 'privateClass',
id: 'privateClass',
constructorDoc: { private: true },
heritage: 'implements parentInterface'
}
];
convertPrivateClassesToInterfaces(docs, false);
expect(docs[0].heritage).toEqual('extends parentInterface');
});
it('should add new injectable reference types, if specified, to the passed in collection', function() {
var docs = [
{
docType: 'class',
name: 'privateClass',
id: 'privateClass',
constructorDoc: { private: true },
heritage: 'implements parentInterface'
}
];
convertPrivateClassesToInterfaces(docs, true);
expect(docs[1]).toEqual({
docType : 'var',
name : 'privateClass',
id : 'privateClass',
returnType : 'InjectableReference'
});
});
});

View File

@ -0,0 +1,3 @@
module.exports = function modules() {
return {};
};

View File

@ -0,0 +1,75 @@
var ts = require('typescript');
var fs = require('fs');
var path = require('canonical-path');
// We need to provide our own version of CompilerHost because we want to set the
// base directory and specify what extensions to consider when trying to load a source
// file
module.exports = function createCompilerHost(log) {
return function createCompilerHost(options, baseDir, extensions) {
return {
getSourceFile: function(fileName, languageVersion, onError) {
var text, resolvedPath, resolvedPathWithExt;
// Strip off the extension and resolve relative to the baseDir
baseFilePath = fileName.replace(/\.[^.]+$/, '');
resolvedPath = path.resolve(baseDir, baseFilePath);
// Iterate through each possible extension and return the first source file that is actually found
for(var i=0; i<extensions.length; i++) {
// Try reading the content from files using each of the given extensions
try {
resolvedPathWithExt = resolvedPath + extensions[i];
log.silly('getSourceFile:', resolvedPathWithExt);
text = fs.readFileSync(resolvedPathWithExt, { encoding: options.charset });
log.debug('found source file:', fileName, resolvedPathWithExt);
return ts.createSourceFile(baseFilePath + extensions[i], text, languageVersion);
}
catch(e) {
// Try again if the file simply did not exist, otherwise report the error as a warning
if(e.code !== 'ENOENT') {
if (onError) onError(e.message);
log.warn('Error reading ' + resolvedPathWithExt + ' : ' + e.message);
}
}
}
},
getDefaultLibFileName: function(options) {
return path.resolve(path.dirname(ts.sys.getExecutingFilePath()), ts.getDefaultLibFileName(options));
},
writeFile: function(fileName, data, writeByteOrderMark, onError) {
// no-op
},
getCurrentDirectory: function() {
return baseDir;
},
useCaseSensitiveFileNames: function() {
return ts.sys.useCaseSensitiveFileNames;
},
getCanonicalFileName: function(fileName) {
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
// otherwise use toLowerCase as a canonical form.
return ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
},
getNewLine: function() {
return ts.sys.newLine;
},
fileExists: function(fileName) {
var resolvedPath = path.resolve(baseDir, fileName);
try {
fs.statSync(resolvedPath);
return true;
} catch (e) {
return false;
}
},
readFile: function(fileName) {
var resolvedPath = path.resolve(baseDir, fileName);
return fs.readFileSync(resolvedPath, { encoding: options.charset });
}
};
};
};

View File

@ -0,0 +1,80 @@
var mockPackage = require('../../mocks/mockPackage');
var Dgeni = require('dgeni');
var path = require('canonical-path');
var ts = require('typescript');
describe('createCompilerHost', function() {
var dgeni, injector, options, host, baseDir, extensions;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
var createCompilerHost = injector.get('createCompilerHost');
options = { charset: 'utf8' };
baseDir = path.resolve(__dirname, '../../mocks/tsParser');
extensions = ['.ts', '.js'];
host = createCompilerHost(options, baseDir, extensions);
});
describe('getSourceFile', function() {
it('should return a SourceFile object for a given path, with fileName relative to baseDir', function() {
var sourceFile = host.getSourceFile('testSrc.ts');
expect(sourceFile.fileName).toEqual('testSrc.ts');
expect(sourceFile.pos).toEqual(0);
expect(sourceFile.text).toEqual(jasmine.any(String));
});
it('should try each of the configured extensions and update the filename to the correct extension', function() {
var sourceFile = host.getSourceFile('testSrc.js');
expect(sourceFile.fileName).toEqual('testSrc.ts');
sourceFile = host.getSourceFile('../mockPackage.ts');
expect(sourceFile.fileName).toEqual('../mockPackage.js');
});
});
describe('getDefaultLibFileName', function() {
it('should return a path to the default library', function() {
expect(host.getDefaultLibFileName(options)).toContain('typescript/lib/lib.d.ts');
});
});
describe('writeFile', function() {
it('should do nothing', function() {
host.writeFile();
});
});
describe('getCurrentDirectory', function() {
it('should return the baseDir', function() {
expect(host.getCurrentDirectory()).toEqual(baseDir);
});
});
describe('useCaseSensitiveFileNames', function() {
it('should return true if the OS is case sensitive', function() {
expect(host.useCaseSensitiveFileNames()).toBe(ts.sys.useCaseSensitiveFileNames);
});
});
describe('getCanonicalFileName', function() {
it('should lower case the filename', function() {
var expectedFilePath = host.useCaseSensitiveFileNames() ? 'SomeFile.ts' : 'somefile.ts';
expect(host.getCanonicalFileName('SomeFile.ts')).toEqual(expectedFilePath);
});
});
describe('getNewLine', function() {
it('should return the newline character for the OS', function() {
expect(host.getNewLine()).toEqual(require('os').EOL);
});
});
});

View File

@ -0,0 +1,49 @@
var ts = require('typescript');
var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
module.exports = function getContent() {
return function(symbol) {
var content = "";
if (!symbol.declarations) return content;
symbol.declarations.forEach(function(declaration) {
// If this is left side of dotted module declaration, there is no doc comment associated with this declaration
if (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.body.kind === ts.SyntaxKind.ModuleDeclaration) {
return content;
}
// If this is dotted module name, get the doc comments from the parent
while (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.parent.kind === ts.SyntaxKind.ModuleDeclaration) {
declaration = declaration.parent;
}
// If this is a variable declaration then we get the doc comments from the grand parent
if (declaration.kind === ts.SyntaxKind.VariableDeclaration) {
declaration = declaration.parent.parent;
}
// Get the source file of this declaration
var sourceFile = ts.getSourceFileOfNode(declaration);
var commentRanges = ts.getJsDocComments(declaration, sourceFile);
if (commentRanges) {
commentRanges.forEach(function(commentRange) {
content += sourceFile.text
.substring(commentRange.pos+ '/**'.length, commentRange.end - '*/'.length)
.replace(LEADING_STAR, '')
.trim();
if (commentRange.hasTrailingNewLine) {
content += '\n';
}
});
}
content += '\n';
});
return content;
};
};

View File

@ -0,0 +1,54 @@
var ts = require('typescript');
module.exports = function getExportDocType(log) {
return function(symbol) {
if(symbol.flags & ts.SymbolFlags.Function) {
return 'function';
}
if(symbol.flags & ts.SymbolFlags.Class) {
return 'class';
}
if(symbol.flags & ts.SymbolFlags.Interface) {
return 'interface';
}
if(symbol.flags & ts.SymbolFlags.ConstEnum) {
return 'enum';
}
if(symbol.flags & ts.SymbolFlags.RegularEnum) {
return 'enum';
}
if(symbol.flags & ts.SymbolFlags.Property) {
return 'module-property';
}
if(symbol.flags & ts.SymbolFlags.TypeAlias) {
return 'type-alias';
}
if(symbol.flags & ts.SymbolFlags.FunctionScopedVariable) {
return 'var';
}
if(symbol.flags & ts.SymbolFlags.BlockScopedVariable) {
return getBlockScopedVariableDocType(symbol);
}
log.warn('getExportDocType(): Unknown symbol type', {
symbolName: symbol.name,
symbolType: symbol.flags,
symbolTarget: symbol.target,
file: ts.getSourceFileOfNode(symbol.declarations[0]).fileName
});
return 'unknown';
};
function getBlockScopedVariableDocType(symbol) {
var node = symbol.valueDeclaration;
while(node) {
if ( node.flags & 0x2000 /* const */) {
return 'const';
}
node = node.parent;
}
return 'let';
}
};

Some files were not shown because too many files have changed in this diff Show More