Compare commits

..

15 Commits

Author SHA1 Message Date
2800683e64 improved fix for non-TS syntax 2015-03-23 16:03:22 -07:00
a1158a06bd Separate task for running ts2dart over all angular sources 2015-03-23 14:39:32 -07:00
35a376acdb Merge branch 'ts2dart' of github.com:alexeagle/angular into ts2dart
Conflicts:
	gulpfile.js
2015-03-23 13:36:39 -07:00
b8f0209ba5 Fix sequencing of ts2dart tasks. 2015-03-23 13:34:24 -07:00
9386bb1cb1 Use the newer dart formatter, which gives better errors 2015-03-20 12:22:12 -07:00
01592ff773 transpile the js files in one package, and fail the build if it fails 2015-03-20 12:22:12 -07:00
2a5d0dd59a Explicitly include gulp-ts2dart in package.json. 2015-03-20 12:22:11 -07:00
e07a2d10c7 Transpile .js files, experimentally. 2015-03-20 12:22:11 -07:00
7234d368e3 First hook in the angular build to run ts2dart.
This expects files we are interested in to have the '.ts' extension.
2015-03-20 12:22:11 -07:00
cdc83730df Use the newer dart formatter, which gives better errors 2015-03-20 12:08:32 -07:00
0f30ce6e92 Merge branch 'ts2dart' of github.com:angular/angular into ts2dart
Conflicts:
	gulpfile.js
2015-03-19 11:01:44 -07:00
fb6b8b22a1 transpile the js files in one package, and fail the build if it fails 2015-03-19 11:00:45 -07:00
853c24f4d4 Transpile .js files, experimentally. 2015-03-18 18:51:43 -07:00
e37f58a228 Merge branch 'master' into ts2dart 2015-03-18 16:01:24 -07:00
e569e0b1ee First hook in the angular build to run ts2dart.
This expects files we are interested in to have the '.ts' extension.
2015-03-18 14:42:18 -07:00
1813 changed files with 44373 additions and 159485 deletions

View File

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

View File

@ -1,3 +0,0 @@
Language: JavaScript
BasedOnStyle: Google
ColumnLimit: 100

26
.gitignore vendored
View File

@ -1,18 +1,11 @@
.DS_STORE
# Dont commit the following directories created by pub.
packages
pubspec.lock
.pub
.packages
/dist/
packages/
.buildlog
node_modules
bower_components
# Or broccoli working directory
tmp
.pub
.DS_STORE
# Or the files created by dart2js.
*.dart.js
@ -21,26 +14,13 @@ tmp
*.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/
*.swo
modules/.settings
.vscode
modules/.vscode
# Don't check in secret files
*secret.js
# Ignore npm debug log
npm-debug.log
/docs/bower_components/
# build-analytics
.build-analytics

1
.nvmrc
View File

@ -1 +0,0 @@
0.12

View File

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

View File

@ -1,128 +1,38 @@
language: node_js
sudo: false
node_js:
- '4.1.1'
branches:
except:
- g3sync
cache:
directories:
- node_modules
- $HOME/.pub-cache
- '0.10'
env:
global:
- 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: 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:
allow_failures:
- 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"
- MODE=js DART_CHANNEL=dev
# Dissabled until Dart v1.9 hits stable
# - MODE=dart DART_CHANNEL=stable
- MODE=dart DART_CHANNEL=dev
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}
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${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:
# 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/build_and_test.sh ${MODE}
after_script:
- ./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_success: change # 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

@ -1,31 +0,0 @@
# Pushing changes into the Angular 2 tree
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
for details about how we maintain a linear commit history, and the rules for committing.
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
Someone with committer access will do the rest.
## The `PR: merge` label and `presubmit-*` branches
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 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}`.
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
When a Travis build succeeds for a presubmit branch named following the convention,
Travis will re-base the commits, merge to master, and close the PR automatically.
Finally, after merge `mary-poppins` removes the presubmit branch.
## Administration
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,7 +18,7 @@ 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 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].
discussion list or [StackOverflow][stackoverflow]. We are also available on [Gitter][gitter].
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can help us by
@ -27,16 +27,27 @@ If you find a bug in the source code or a mistake in the documentation, you can
## <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. 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:
Repository][github]. If you would like to *implement* a new feature then consider what kind of
change it is:
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
and help you to craft the change so that it is successfully accepted into the project.
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## <a name="docs"></a> Want a Doc Fix?
If you want to help improve the docs, then consider what kind of improvement it is:
* For **Major Changes**, it's a good idea to let others know what you're working on to
minimize duplication of effort. Before starting, check out the issue queue for
issues labeled [#docs](https://github.com/angular/angular/labels/%23docs).
Comment on an issue to let others know what you're working on, or [create a new issue](#submit-issue)
if your work doesn't fit within the scope of any of the existing doc issues.
Please build and test the documentation before [submitting the Pull Request](#submit-pr), to be sure
you haven't accidentally introduced any layout or formatting issues. Also ensure that your commit
message is labeled "docs" and follows the [Commit Message Guidelines](#commit) given below.
* For **Small Changes**, there is no need to file an issue first. Simply [submit a Pull Request](#submit-pr).
## <a name="submit"></a> Submission Guidelines
### <a name="submit-issue"></a> Submitting an Issue
@ -49,6 +60,7 @@ chances of your issue being dealt with quickly:
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
* **Motivation for or Use Case** - explain why this is a bug for you
* **Angular Version(s)** - is it a regression?
* **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.
@ -59,7 +71,7 @@ chances of your issue being dealt with quickly:
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
* Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
* Search [GitHub](https://github.com/angular/angular.dart/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort.
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this.
@ -135,9 +147,9 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
* All public API methods **must be documented**. (Details TBC).
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
**100 characters**. An automated formatter is available, see
[DEVELOPER.md](DEVELOPER.md#clang-format).
* With the exceptions listed below, we follow the rules contained in
[Google's JavaScript Style Guide][js-style-guide]:
* Wrap all code at **100 characters**.
## <a name="commit"></a> Commit Message Guidelines
@ -157,14 +169,9 @@ format that includes a **type**, a **scope** and a **subject**:
<footer>
```
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.
### 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.
### Type
Must be one of the following:
@ -173,14 +180,14 @@ Must be one of the following:
* **docs**: Documentation only changes
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **refactor**: A code change that neither fixes a bug or adds a feature
* **perf**: A code change that improves performance
* **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
The scope could be anything specifying place of the commit change. For example
`Compiler`, `ElementInjector`, etc.
### Subject
@ -198,7 +205,6 @@ The body should include the motivation for the change and contrast this with pre
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
A detailed explanation can be found in this [document][commit-message-format].

View File

@ -3,63 +3,56 @@
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)
* [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 [contributing guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
for how to contribute your own code to
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
if you'd like to contribute to Angular.
1. [Prerequisite Software](#prerequisite-software)
2. [Getting the Sources](#getting-the-sources)
3. [Environment Variable Setup](#environment-variable-setup)
4. [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
5. [Running Tests Locally](#running-tests-locally)
6. [Project Information](#project-information)
7. [CI using Travis](#ci-using-travis)
8. [Debugging](#debugging)
## Prerequisite Software
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
* [Dart](https://www.dartlang.org) (version `>=1.9.0-dev.8.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).`
download [page for instructions](https://www.dartlang.org/tools/download.html); note that you can
download both **stable** and **dev** channel versions from the [download
archive](https://www.dartlang.org/tools/download-archive).
* [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](http://git-scm.com) and/or the **Github app** (for [Mac](http://mac.github.com) or
[Windows](http://windows.github.com)): the [Github Guide to Installing
Git](https://help.github.com/articles/set-up-git) is a good source of information.
* [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.
* [Node.js](http://nodejs.org) which is used to run a development web server, run tests, and
generate distributable files. We also use Node's Package Manager (`npm`). Depending on your
system, you can install Node either from source or as a pre-packaged bundle.
* [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
Fork and clone the Angular repository:
Forking and cloning the Angular repository:
1. Login to your GitHub account or create one by following the instructions given
1. Login to your Github account or create one by following the instructions given
[here](https://github.com/signup/free).
2. [Fork](http://help.github.com/forking) the [main Angular
repository](https://github.com/angular/angular).
3. Clone your fork of the Angular repository and define an `upstream` remote pointing back to
the Angular repository that you forked in the first place.
the Angular repository that you forked in the first place:
```shell
# Clone your GitHub repository:
# Clone your Github repository:
git clone git@github.com:<github username>/angular.git
# Go to the Angular directory:
@ -97,31 +90,32 @@ PATH+=":$DART_SDK/bin"
## Installing NPM Modules and Dart Packages
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
Next, install the modules and packages needed to build Angular and run tests:
```shell
# Install Angular project dependencies (package.json)
npm install
# Ensure protractor has the latest webdriver
$(npm bin)/webdriver-manager update
# Install Dart packages
pub get
```
**Optional**: In this document, we make use of project local `npm` package scripts and binaries
(stored under `./node_modules/.bin`) by prefixing these command invocations with `$(npm bin)`; in
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by either:
*Option 1*: globally installing these two packages as follows:
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by
globally installing these two packages as follows:
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
* `npm install -g protractor` (you might need to prefix this command with `sudo`)
Since global installs can become stale, and required versions can vary by project, we avoid their
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`.
Since global installs can become stale, we avoid their use in these instructions.
## Build commands
To build Angular and prepare tests, run:
To build Angular and prepare tests run
```shell
$(npm bin)/gulp build
@ -130,69 +124,42 @@ $(npm bin)/gulp build
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`.
You can selectively build either the JS or Dart versions as follows:
* `$(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
$(npm bin)/gulp docs/typings
```
To clean out the `dist` folder, run:
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`
To clean out the `dist` folder use:
```shell
$(npm bin)/gulp clean
```
## Running Tests Locally
### Full test suite
### Basic tests
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests
that run on Travis.
1. `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e. karma
watches the test files for changes and re-runs tests when files are updated).
2. `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**
3. `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
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:
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**: 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** for transpiler tests: The karma preprocessor is setup in a way so that after every test
run the transpiler is reloaded. With that it is possible to make changes to the preprocessor and
run the tests without exiting karma (just touch a test file that you would like to run).
**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
### 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).
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver).
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js 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`
@ -200,79 +167,13 @@ Angular specific command line options when running protractor:
### 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)
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs 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
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
$(npm bin)/gulp docs/angular.io
```
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
### Folder structure
@ -283,18 +184,16 @@ You can serve the generated documentation to check how it would render on [angul
### File suffixes
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
* `*.dart`: Dart files that don't get transpiled
* `*.js`: javascript files that get transpiled to Dart and EcmaScript 5
* `*.es6`: javascript files that get transpiled only to EcmaScript 5
* `*.es5`: javascript files that don't get transpiled
* `*.dart`: dart files that don't get transpiled
## CI using Travis
For instructions on setting up Continuous Integration using Travis, see the instructions given
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
## Transforming Dart code
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
## Debugging
### Debug the transpiler
@ -320,18 +219,17 @@ Notes:
If you need to debug the tests:
- add a `debugger;` statement to the test you want to debug (or the source code),
- add a `debugger;` statement to the test you want to debug (oe 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
- open the dev tools and press F5,
- the execution halt 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.
You can create a Karma run config from WebStorm.
Then in the "Run" menu, press "Debug 'karma-js.conf.js'", WebStorm will stop in the generated code
on the `debugger;` statement.
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.

View File

@ -1,32 +0,0 @@
Naming Conventions in Angular2
---
In general Angular2 should follow TypeScript naming conventions.
See: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
Classes:
- Example: `Compiler`, `ApplicationMetadata`
- Camel case with first letter upper-case
- In general prefer single words. (This is so that when appending `Proto` or `Factory` the class
is still reasonable to work with.)
- Should not end with `Impl` or any other word which describes a specific implementation of an
interface.
Interfaces:
- Follow the same rules as Classes
- Should not have `I` or `Interface` in the name or any other way of identifying it as an interface.
Methods and functions:
- Example: `bootstrap`, `someMethod`
- Should be camel case with first lower case
Constants
- Example: `CORE_DIRECTIVES`
- Should be all uppercase with SNAKE_CASE

View File

@ -1,26 +1,15 @@
[![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)](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)
Angular
Angular [![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)
=========
Angular is a development platform for building mobile and desktop web applications. This is the
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
Angular 2 is currently in **Alpha 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
@ -29,15 +18,35 @@ 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).
Want to file a bug, or contribute some code or improve documentation? Excellent! Read up on our
guidelines for [contributing][contributing].
## Examples
To see the examples, first build the project as described
[here](http://github.com/angular/angular/blob/master/DEVELOPER.md).
### Hello World Example
This example consists of three basic pieces - a component, a decorator and a
service. They are all constructed via injection. For more information see the
comments in the source `modules/examples/src/hello_world/index.js`.
You can build this example as either JS or Dart app:
* JS:
* `$(npm bin)/gulp serve.js.dev`, and
* open `localhost:8000/examples/src/hello_world/` in Chrome.
* Dart:
* `$(npm bin)/gulp serve/examples.dart`, and
* open `localhost:8080/src/hello_world` in Chrome (for dart2js) or
[Dartium][dartium] (for Dart VM).
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
[dart]: http://www.dartlang.org
[dartium]: http://www.dartlang.org/tools/dartium
[download]: http://angular.io/download/
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
[download]: http://angular.io/download
[ng2]: http://angular.io
[ngDart]: http://angulardart.org
[ngJS]: http://angularjs.org

View File

@ -1,4 +0,0 @@
# Developer Tools for Angular 2
- [JavaScript](TOOLS_JS.md)
- [Dart](TOOLS_DART.md)

View File

@ -1,324 +0,0 @@
# 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 -->

View File

@ -1,140 +0,0 @@
# 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,168 +0,0 @@
# Triage Process and Github Labels for Angular 2
This document describes how the Angular team uses labels and milestones to triage issues on github.
# Issues and PRs
## Triaged vs Untriaged Issues
Every triaged issue must have four attributes assigned to 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.
Untriaged issues are any issues in the queue that don't yet have these four attributes.
You can view a report of untriaged issues here, in our
[Angular Triage Dashboard](http://mhevery.github.io/github_issues/).
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
Any issue that is being worked on must have:
* An `assignee`: The person doing the work.
* A `Milestone`: When we expect to complete this work.
We aim to only have at most three milestones open at a time:
* Closing Milestone: A milestone with a very small number of issues, about to release.
* Current Milestone: Work that we plan to complete within one week.
* Next Milestone: Work that is > 1 week but current for the team.
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 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.
Every triaged PR must have a `pr_action` label assigned to it and an assignee:
* `pr_action: review` -- work is complete and comment is needed from the assignee.
* `pr_action: cleanup` -- more work is needed from the current assignee.
* `pr_action: discuss` -- discussion is needed, to be led by the current assignee.
* `pr_action: merge` -- the PR should be merged. Add this to a PR when you would like to
trigger automatic merging following a successful build. This is described in [COMMITTER.md](COMMITTER.md).
In addition, PRs can have the following states:
* `pr_state: LGTM` -- PR may have outstanding changes but does not require further review.
* `pr_state: WIP` -- PR is experimental or rapidly changing. Not ready for review or triage.
* `pr_state: blocked` -- PR is blocked on an issue or other PR. Not ready for review or triage.
Note that an LGTM state does not mean a PR is ready to merge: for example, a reviewer might set the
LGTM state but request a minor tweak that doesn't need further review, e.g., a rebase or small
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
### action:design
More active discussion is needed before the issue can be worked on further. Typically used for
`type: feature` or `type: RFC/discussion/question`
[See all issues that need discussion](https://github.com/angular/angular/labels/action:%20Design)
### cla
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,7 @@
{
"name": "angular2",
"version": "0.0.0",
"dependencies": {
"polymer": "dart-lang/polymer_js#0.8.0-preview"
}
}
}

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

@ -0,0 +1,366 @@
.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;
}

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>
<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 src="nav.currentPage.partial"></ng-include>
</md-content>
</section>
</body>
</html>

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

@ -0,0 +1,47 @@
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) {
path = path.replace(/^\//, '');
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"
}
}

147
docs/dgeni-package/index.js Normal file
View File

@ -0,0 +1,147 @@
require('../../tools/transpiler/index.js').init();
var Package = require('dgeni').Package;
var jsdocPackage = require('dgeni-packages/jsdoc');
var nunjucksPackage = require('dgeni-packages/nunjucks');
var path = require('canonical-path');
var PARTIAL_PATH = 'partials';
var MODULES_DOCS_PATH = PARTIAL_PATH + '/modules';
var GUIDES_PATH = PARTIAL_PATH + '/guides';
// Define the dgeni package for generating the docs
module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
// Register the services and file readers
.factory(require('./services/modules'))
.factory(require('./services/atParser'))
.factory(require('./services/getJSDocComment'))
.factory(require('./services/SourceFile'))
.factory(require('./services/TraceurParser'))
.factory(require('./services/traceurOptions'))
.factory(require('./services/ParseTreeVisitor'))
.factory(require('./services/AttachCommentTreeVisitor'))
.factory(require('./services/ExportTreeVisitor'))
.factory(require('./readers/atScript'))
.factory(require('./readers/ngdoc'))
.factory('EXPORT_DOC_TYPES', function() {
return [
'class',
'function',
'var',
'const'
];
})
// Register the processors
.processor(require('./processors/generateDocsFromComments'))
.processor(require('./processors/processModuleDocs'))
.processor(require('./processors/processClassDocs'))
.processor(require('./processors/generateNavigationDoc'))
.processor(require('./processors/extractTitleFromGuides'))
// Configure the log service
.config(function(log) {
log.level = 'info';
})
// Configure file reading
.config(function(readFilesProcessor, atScriptFileReader, ngdocFileReader) {
readFilesProcessor.fileReaders = [atScriptFileReader, ngdocFileReader];
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
readFilesProcessor.sourceFiles = [
{ include: 'modules/*/*.js', basePath: 'modules' },
{ include: 'modules/*/src/**/*.js', basePath: 'modules' },
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
];
})
// 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, EXPORT_DOC_TYPES) {
computeIdsProcessor.idTemplates.push({
docTypes: EXPORT_DOC_TYPES,
idTemplate: '${moduleDoc.id}.${name}',
getAliases: function(doc) { return [doc.id]; }
});
computeIdsProcessor.idTemplates.push({
docTypes: ['member'],
idTemplate: '${classDoc.id}.${name}',
getAliases: function(doc) { return [doc.id]; }
});
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: ['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'
});
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
});
computePathsProcessor.pathTemplates.push({
docTypes: ['guide'],
pathTemplate: '${id}',
outputPathTemplate: GUIDES_PATH + '/${id}.html'
});
});

View File

@ -5,7 +5,6 @@ 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 {}; });
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); });
};

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,34 @@
var _ = require('lodash');
module.exports = function generateDocsFromComments(log) {
return {
$runAfter: ['files-read'],
$runBefore: ['parsing-tags'],
$process: function(docs) {
var commentDocs = [];
docs = _.filter(docs, function(doc) {
if (doc.docType !== 'atScriptFile') {
return true;
} else {
_.forEach(doc.fileInfo.comments, function(comment) {
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
// Create a doc from this comment
commentDocs.push({
fileInfo: doc.fileInfo,
startingLine: comment.range.start.line,
endingLine: comment.range.end.line,
content: commentBody,
codeTree: comment.treeAfter,
docType: 'atScriptDoc'
});
});
});
}
});
return docs.concat(commentDocs);
}
};
};

View File

@ -0,0 +1,66 @@
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) {
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,47 @@
var _ = require('lodash');
module.exports = function processClassDocs(log, getJSDocComment) {
return {
$runAfter: ['processModuleDocs'],
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
ignorePrivateMembers: false,
$process: function(docs) {
var memberDocs = [];
var ignorePrivateMembers = this.ignorePrivateMembers;
_.forEach(docs, function(classDoc) {
if ( classDoc.docType === 'class' ) {
classDoc.members = [];
// Create a new doc for each member of the class
_.forEach(classDoc.elements, function(memberDoc) {
if (ignorePrivateMembers && memberDoc.name.literalToken.value.charAt(0) === '_') return;
classDoc.members.push(memberDoc);
memberDocs.push(memberDoc);
memberDoc.docType = 'member';
memberDoc.classDoc = classDoc;
memberDoc.name = memberDoc.name.literalToken.value;
if (memberDoc.commentBefore ) {
// If this export has a comment, remove it from the list of
// comments collected in the module
var index = classDoc.moduleDoc.comments.indexOf(memberDoc.commentBefore);
if ( index !== -1 ) {
classDoc.moduleDoc.comments.splice(index, 1);
}
_.assign(memberDoc, getJSDocComment(memberDoc.commentBefore));
}
});
}
});
return docs.concat(memberDocs);
}
};
};

View File

@ -0,0 +1,46 @@
var _ = require('lodash');
module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComment) {
return {
$runAfter: ['files-read'],
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
$process: function(docs) {
var exportDocs = [];
_.forEach(docs, function(doc) {
if ( doc.docType === 'module' ) {
log.debug('processing', doc.moduleTree.moduleName);
doc.exports = [];
if ( doc.moduleTree.visit ) {
var visitor = new ExportTreeVisitor();
visitor.visit(doc.moduleTree);
_.forEach(visitor.exports, function(exportDoc) {
doc.exports.push(exportDoc);
exportDocs.push(exportDoc);
exportDoc.moduleDoc = doc;
if (exportDoc.comment) {
// If this export has a comment, remove it from the list of
// comments collected in the module
var index = doc.comments.indexOf(exportDoc.comment);
if ( index !== -1 ) {
doc.comments.splice(index, 1);
}
_.assign(exportDoc, getJSDocComment(exportDoc.comment));
}
});
}
}
});
return docs.concat(exportDocs);
}
};
};

View File

@ -0,0 +1,32 @@
var path = require('canonical-path');
/**
* @dgService atScriptFileReader
* @description
* This file reader will create a simple doc for each
* file including a code AST of the AtScript in the file.
*/
module.exports = function atScriptFileReader(log, atParser, modules) {
var reader = {
name: 'atScriptFileReader',
defaultPattern: /\.js$/,
getDocs: function(fileInfo) {
var moduleDoc = atParser.parseModule(fileInfo);
moduleDoc.docType = 'module';
moduleDoc.id = moduleDoc.moduleTree.moduleName;
moduleDoc.aliases = [moduleDoc.id];
modules[moduleDoc.id] = moduleDoc;
// Readers return a collection of docs read from the file
// but in this read there is only one document (module) to return
return [moduleDoc];
}
};
return reader;
};

View File

@ -0,0 +1,55 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
describe('atScript file reader', function() {
var dgeni, injector, reader;
var fileContent =
'import {CONST} from "facade/lang";\n' +
'\n' +
'/**\n' +
'* A parameter annotation that creates a synchronous eager dependency.\n' +
'*\n' +
'* class AComponent {\n' +
'* constructor(@Inject("aServiceToken") aService) {}\n' +
'* }\n' +
'*\n' +
'*/\n' +
'export class Inject {\n' +
'token;\n' +
'@CONST()\n' +
'constructor(token) {\n' +
'this.token = token;\n' +
'}\n' +
'}';
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
reader = injector.get('atScriptFileReader');
});
it('should provide a default pattern', function() {
expect(reader.defaultPattern).toEqual(/\.js$/);
});
it('should parse the file using the atParser and return a single doc', function() {
var atParser = injector.get('atParser');
spyOn(atParser, 'parseModule').and.callThrough();
var docs = reader.getDocs({
content: fileContent,
relativePath: 'di/src/annotations.js'
});
expect(atParser.parseModule).toHaveBeenCalled();
expect(docs.length).toEqual(1);
expect(docs[0].docType).toEqual('module');
});
});

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,47 @@
module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
function AttachCommentTreeVisitorImpl() {
ParseTreeVisitor.call(this);
}
AttachCommentTreeVisitorImpl.prototype = {
__proto__: ParseTreeVisitor.prototype,
visit: function(tree, comments) {
this.comments = comments;
this.index = 0;
this.currentComment = this.comments[this.index];
if (this.currentComment) log.silly('comment: ' +
this.currentComment.range.start.line + ' - ' +
this.currentComment.range.end.line + ' : ' +
this.currentComment.range.toString());
ParseTreeVisitor.prototype.visit.call(this, tree);
},
// Really we ought to subclass ParseTreeVisitor but this is fiddly in ES5 so
// it is easier to simply override the prototype's method on the instance
visitAny: function(tree) {
if (tree && tree.location && tree.location.start && this.currentComment &&
this.currentComment.range.end.offset < tree.location.start.offset) {
log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
while (this.currentComment &&
this.currentComment.range.end.offset < tree.location.start.offset) {
log.silly('comment: ' + this.currentComment.range.start.line + ' - ' +
this.currentComment.range.end.line + ' : ' +
this.currentComment.range.toString());
tree.commentBefore = this.currentComment;
this.currentComment.treeAfter = tree;
this.index++;
this.currentComment = this.comments[this.index];
}
}
return ParseTreeVisitor.prototype.visitAny.call(this, tree);
}
};
return AttachCommentTreeVisitorImpl;
};

View File

@ -0,0 +1,103 @@
module.exports = function ExportTreeVisitor(ParseTreeVisitor, log) {
function ExportTreeVisitorImpl() {
ParseTreeVisitor.call(this);
}
ExportTreeVisitorImpl.prototype = {
__proto__: ParseTreeVisitor.prototype,
visitExportDeclaration: function(tree) {
// We are entering an export declaration - create an object to track it
this.currentExport = {
comment: tree.commentBefore,
location: tree.location
};
log.silly('enter', tree.type, tree.commentBefore ? 'has comment' : '');
ParseTreeVisitor.prototype.visitExportDeclaration.call(this, tree);
log.silly('exit', this.currentExport);
if(this.currentExport) {
// We are exiting the export declaration - store the export object
this.exports.push(this.currentExport);
}
this.currentExport = null;
},
visitVariableDeclaration: function(tree) {
if ( this.currentExport ) {
this.updateExport(tree);
this.currentExport.docType = 'var';
this.currentExport.name = tree.lvalue.identifierToken.value;
this.currentExport.variableDeclaration = tree;
}
},
visitFunctionDeclaration: function(tree) {
if ( this.currentExport ) {
this.updateExport(tree);
this.currentExport.name = tree.name.identifierToken.value;
this.currentExport.functionKind = tree.functionKind;
this.currentExport.parameters = tree.parameterList.parameters;
this.currentExport.typeAnnotation = tree.typeAnnotation;
this.currentExport.annotations = tree.annotations;
this.currentExport.docType = 'function';
log.silly(tree.type, tree.commentBefore ? 'has comment' : '');
}
},
visitClassDeclaration: function(tree) {
if ( this.currentExport ) {
this.updateExport(tree);
this.currentExport.name = tree.name.identifierToken.value;
this.currentExport.superClass = tree.superClass;
this.currentExport.annotations = tree.annotations;
this.currentExport.elements = tree.elements;
this.currentExport.docType = 'class';
}
},
visitAsyncFunctionDeclaration: function(tree) {
if ( this.currentExport ) {
this.updateExport(tree);
}
},
visitExportDefault: function(tree) {
if ( this.currentExport ) {
this.updateExport(tree);
this.currentExport.name = 'DEFAULT';
this.currentExport.defaultExport = tree;
// Default exports are either classes, functions or expressions
// So we let the super class continue down...
ParseTreeVisitor.prototype.visitExportDefault.call(this, tree);
}
},
visitNamedExport: function(tree) {
this.currentExport = null;
// if ( this.currentExport ) {
// this.updateExport(tree);
// this.currentExport.namedExport = tree;
// this.currentExport.name = 'NAMED_EXPORT';
// // TODO: work out this bit!!
// // We need to cope with any export specifiers in the named export
// }
},
// TODO - if the export is an expression, find the thing that is being
// exported and use it and its comments for docs
updateExport: function(tree) {
this.currentExport.comment = this.currentExport.comment || tree.commentBefore;
this.currentExport.docType = tree.type;
},
visit: function(tree) {
this.exports = [];
ParseTreeVisitor.prototype.visit.call(this, tree);
}
};
return ExportTreeVisitorImpl;
};

View File

@ -0,0 +1,6 @@
var traceur = require('traceur/src/node/traceur.js');
module.exports = function ParseTreeVisitor() {
console.log(System.map.traceur);
return System.get(System.map.traceur + '/src/syntax/ParseTreeVisitor.js').ParseTreeVisitor;
};

View File

@ -0,0 +1,5 @@
var traceur = require('traceur/src/node/traceur.js');
module.exports = function SourceFile() {
return System.get(System.map.traceur + '/src/syntax/SourceFile.js').SourceFile;
};

View File

@ -0,0 +1,3 @@
module.exports = function TraceurParser() {
return System.get('transpiler/src/parser').Parser;
};

View File

@ -0,0 +1,74 @@
var file2modulename = require('../../../tools/build/file2modulename');
/**
* Wrapper around traceur that can parse the contents of a file
*/
module.exports = function atParser(AttachCommentTreeVisitor, SourceFile, TraceurParser, traceurOptions, log) {
var service = {
/**
* The options to pass to traceur
*/
traceurOptions: {
annotations: true, // parse annotations
types: true, // parse types
memberVariables: true, // parse class fields
commentCallback: true // handle comments
},
/**
* Parse a module AST from the contents of a file.
* @param {Object} fileInfo information about the file to parse
* @return { { moduleTree: Object, comments: Array } } An object containing the parsed module
* AST and an array of all the comments found in the file
*/
parseModule: parseModule
};
return service;
// Parse the contents of the file using traceur
function parseModule(fileInfo) {
var moduleName = file2modulename(fileInfo.relativePath);
var sourceFile = new SourceFile(moduleName, fileInfo.content);
var comments = [];
var moduleTree;
var parser = new TraceurParser(sourceFile);
// Configure the parser
parser.handleComment = function(range) {
comments.push({ range: range });
};
traceurOptions.setFromObject(service.traceurOptions);
try {
// Parse the file as a module, attaching the comments
moduleTree = parser.parseModule();
attachComments(moduleTree, comments);
} catch(ex) {
// HACK: sometime traceur crashes for various reasons including
// Not Yet Implemented (NYI)!
log.error(ex.stack);
moduleTree = {};
}
log.debug(moduleName);
moduleTree.moduleName = moduleName;
// We return the module AST but also a collection of all the comments
// since it can be helpful to iterate through them without having to
// traverse the AST again
return {
moduleTree: moduleTree,
comments: comments
};
}
// attach the comments to their nearest code tree
function attachComments(tree, comments) {
var visitor = new AttachCommentTreeVisitor();
// Visit every node of the tree using our custom method
visitor.visit(tree, comments);
}
};

View File

@ -0,0 +1,80 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
describe('atParser service', function() {
var dgeni, injector, parser;
var fileContent =
'import {CONST} from "facade/lang";\n' +
'\n' +
'/**\n' +
'* A parameter annotation that creates a synchronous eager dependency.\n' +
'*\n' +
'* class AComponent {\n' +
'* constructor(@Inject("aServiceToken") aService) {}\n' +
'* }\n' +
'*\n' +
'*/\n' +
'export class Inject {\n' +
'token;\n' +
'@CONST()\n' +
'constructor({a,b}:{a:string, b:string}) {\n' +
'this.token = a;\n' +
'}\n' +
'}';
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
parser = injector.get('atParser');
});
it('should extract the comments from the file', function() {
var result = parser.parseModule({
content: fileContent,
relativePath: 'di/src/annotations.js'
});
expect(result.comments[0].range.toString()).toEqual(
'/**\n' +
'* A parameter annotation that creates a synchronous eager dependency.\n' +
'*\n' +
'* class AComponent {\n' +
'* constructor(@Inject("aServiceToken") aService) {}\n' +
'* }\n' +
'*\n' +
'*/'
);
});
it('should extract a module AST from the file', function() {
var result = parser.parseModule({
content: fileContent,
relativePath: 'di/src/annotations.js'
});
expect(result.moduleTree.moduleName).toEqual('di/src/annotations');
expect(result.moduleTree.scriptItemList[0].type).toEqual('IMPORT_DECLARATION');
expect(result.moduleTree.scriptItemList[1].type).toEqual('EXPORT_DECLARATION');
});
it('should attach comments to their following AST', function() {
var result = parser.parseModule({
content: fileContent,
relativePath: 'di/src/annotations.js'
});
expect(result.moduleTree.scriptItemList[1].commentBefore.range.toString()).toEqual(
'/**\n' +
'* A parameter annotation that creates a synchronous eager dependency.\n' +
'*\n' +
'* class AComponent {\n' +
'* constructor(@Inject("aServiceToken") aService) {}\n' +
'* }\n' +
'*\n' +
'*/'
);
});
});

View File

@ -0,0 +1,28 @@
var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
/**
* Extact comment info from a comment object
* @param {Object} comment object to process
* @return { {startingLine, endingLine, content, codeTree}= } a comment info object
* or undefined if the comment is not a jsdoc style comment
*/
module.exports = function getJSDocComment() {
return function(comment) {
var commentInfo;
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
commentBody = commentBody.replace(LEADING_STAR, '').trim();
commentInfo = {
startingLine: comment.range.start.line,
endingLine: comment.range.end.line,
content: commentBody,
codeTree: comment.treeAfter
};
});
return commentInfo;
};
};

View File

@ -0,0 +1,67 @@
var mockPackage = require('../mocks/mockPackage');
var Dgeni = require('dgeni');
describe('getJSDocComment service', function() {
var dgeni, injector, getJSDocComment;
function createComment(commentString, start, end, codeTree) {
return {
range: {
toString: function() { return commentString; },
start: { line: start },
end: { line: end },
},
treeAfter: codeTree
};
}
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
getJSDocComment = injector.get('getJSDocComment');
});
it('should only return an object if the comment starts with /** and ends with */', function() {
var result = getJSDocComment(createComment('/** this is a jsdoc comment */'));
expect(result).toBeDefined();
result = getJSDocComment(createComment('/* this is a normal comment */'));
expect(result).toBeUndefined();
result = getJSDocComment(createComment('this is not a valid comment */'));
expect(result).toBeUndefined();
result = getJSDocComment(createComment('nor is this'));
expect(result).toBeUndefined();
result = getJSDocComment(createComment('/* or even this'));
expect(result).toBeUndefined();
result = getJSDocComment(createComment('/** and this'));
expect(result).toBeUndefined();
});
it('should return a result that contains info about the comment', function() {
var codeTree = {};
var result = getJSDocComment(createComment('/** this is a comment */', 10, 20, codeTree));
expect(result.startingLine).toEqual(10);
expect(result.endingLine).toEqual(20);
expect(result.codeTree).toBe(codeTree);
});
it('should strip off leading stars from each line', function() {
var result = getJSDocComment(createComment(
'/** this is a jsdoc comment */\n' +
' *\n' +
' * some content\n' +
' */'
));
expect(result.content).toEqual(
'this is a jsdoc comment */\n' +
'\n' +
'some content'
);
});
});

View File

@ -0,0 +1,3 @@
module.exports = function traceurOptions() {
return System.get(System.map.traceur + '/src/Options.js').options;
};

View File

@ -0,0 +1,14 @@
{% extends 'layout/base.template.html' %}
{% block body %}
<h1>{$ doc.name $} <span class="type">class</span></h1>
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
<p>{$ doc.description | marked $}</p>
<h2>Members</h2>
{% for member in doc.members %}
<h3>{$ member.name $}</h3>
<p>{$ member.description | marked $}</p>
{% endfor %}
{% 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,3 @@
angular.module('{$ doc.moduleName $}', [])
.value('{$ doc.serviceName $}', {$ doc.value | json $});

View File

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

View File

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

View File

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

View File

@ -0,0 +1,21 @@
var Package = require('dgeni').Package;
var basePackage = require('../dgeni-package');
module.exports = new Package('angular-public', [basePackage])
.processor(require('./processors/filterPublicDocs'))
.config(function(parseTagsProcessor) {
parseTagsProcessor.tagDefinitions.push({ name: 'publicModule' });
})
.config(function(processClassDocs, filterPublicDocs, EXPORT_DOC_TYPES) {
processClassDocs.ignorePrivateMembers = true;
filterPublicDocs.docTypes = EXPORT_DOC_TYPES;
})
// Configure file writing
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/public_docs';
});

View File

@ -0,0 +1,51 @@
var _ = require('lodash');
module.exports = function filterPublicDocs(modules) {
return {
$runAfter: ['tags-parsed'],
$runBefore: ['computing-ids'],
docTypes: [],
$validate: {
docTypes: { presence: true }
},
$process: function(docs) {
docTypes = this.docTypes;
docs = _.filter(docs, function(doc) {
if (docTypes.indexOf(doc.docType) === -1) return true;
if (!doc.publicModule) return false;
updateModule(doc);
return true;
});
docs = _.filter(docs, function(doc) {
return doc.docType !== 'module' || doc.isPublic;
});
return docs;
}
};
function updateModule(classDoc) {
var originalModule = classDoc.moduleDoc;
var publicModule = modules[classDoc.publicModule];
if (!publicModule) {
throw new Error('Missing module definition: "' + classDoc.publicModule + '"\n' +
'Referenced in class: "' + classDoc.moduleDoc.id + '/' + classDoc.name + '"');
}
publicModule.isPublic = true;
_.remove(classDoc.moduleDoc.exports, function(doc) { return doc === classDoc; });
classDoc.moduleDoc = publicModule;
publicModule.exports.push(classDoc);
}
};

View File

@ -1,121 +0,0 @@
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/testing.ts'
];
readTypeScriptModules.basePath = path.resolve(path.resolve(__dirname, '../../modules'));
createTypeDefinitionFile.typeDefinitions = [
{
id: 'angular2/angular2',
references: [],
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', ElementRef: 'ng.ElementRef', DynamicComponentLoader: 'ng.DynamicComponentLoader'},
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/testing',
references: ['./angular2.d.ts'],
remapTypes: { Type: 'ng.Type', Binding: 'ng.Binding', Provider: 'ng.Provider', ViewMetadata: 'ng.ViewMetadata', Injector: 'ng.Injector',
Predicate: 'ng.Predicate', ElementRef: 'ng.ElementRef', DebugElement: 'ng.DebugElement',
InjectableReference: 'ng.InjectableReference', ComponentRef: 'ng.ComponentRef' },
modules: {'angular2/testing': {namespace: 'ngTesting', id: 'angular2/testing'}}
}
];
})
.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

@ -1,11 +0,0 @@
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

@ -1,220 +0,0 @@
'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 || ast.internal) return;
buffer.push('\n');
this.comment(buffer, ast.content);
if (ast.isStatic) buffer.push('static ');
this.declaration(buffer, ast);
},
interfaceOrClass: function(buffer, ast, isInterface) {
if (ast.abstract) {
buffer.push('abstract ');
}
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

@ -1,72 +0,0 @@
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

@ -1,53 +0,0 @@
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 @internal 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: { internal: 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

@ -1,17 +0,0 @@
{% 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

@ -1,43 +0,0 @@
{%- 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

@ -1,74 +0,0 @@
var basePackage = require('dgeni-packages/base');
var jsdocPackage = require('dgeni-packages/jsdoc');
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, jsdocPackage])
// 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';
})
.config(function(parseTagsProcessor) {
parseTagsProcessor.tagDefinitions.push({ name: 'internal', transforms: function() { return true; } });
})
// 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,34 +0,0 @@
/**
* @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

@ -1,413 +0,0 @@
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;
}
if (isAbstract(exportSymbol)) {
exportDoc.abstract = true;
}
// 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 isAbstract(symbol) {
var declaration = symbol.valueDeclaration || symbol.declarations[0];
return declaration.flags & ts.NodeFlags.Abstract;
}
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

@ -1,127 +0,0 @@
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

@ -1,31 +0,0 @@
var _ = require('lodash');
module.exports = function convertPrivateClassesToInterfaces() {
return function(exportDocs, addInjectableReference) {
_.forEach(exportDocs, function(exportDoc) {
// Search for classes with a constructor marked as `@internal`
if (exportDoc.docType === 'class' && exportDoc.constructorDoc && exportDoc.constructorDoc.internal) {
// 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

@ -1,76 +0,0 @@
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 @internal class docs to interface docs', function() {
var docs = [
{
docType: 'class',
name: 'privateClass',
id: 'privateClass',
constructorDoc: { internal: true }
}
];
convertPrivateClassesToInterfaces(docs, false);
expect(docs[0].docType).toEqual('interface');
});
it('should not touch non-internal 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: { internal: 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: { internal: true },
heritage: 'implements parentInterface'
}
];
convertPrivateClassesToInterfaces(docs, true);
expect(docs[1]).toEqual({
docType : 'var',
name : 'privateClass',
id : 'privateClass',
returnType : 'InjectableReference'
});
});
});

View File

@ -1,75 +0,0 @@
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

@ -1,80 +0,0 @@
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

@ -1,49 +0,0 @@
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

@ -1,54 +0,0 @@
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';
}
};

View File

@ -1,20 +0,0 @@
var path = require('canonical-path');
var ts = require('typescript');
module.exports = function getFileInfo(log) {
return function (symbol, basePath) {
var fileName = ts.getSourceFileOfNode(symbol.declarations[0]).fileName;
var file = path.resolve(basePath, fileName);
var fileInfo = {
filePath: file,
baseName: path.basename(file, path.extname(file)),
extension: path.extname(file).replace(/^\./, ''),
basePath: basePath,
relativePath: fileName,
projectRelativePath: fileName
};
return fileInfo;
};
};

View File

@ -1,74 +0,0 @@
var ts = require('typescript');
var path = require('canonical-path');
module.exports = function tsParser(createCompilerHost, log) {
return {
// These are the extension that we should consider when trying to load a module
// During migration from Traceur, there is a mix of `.ts`, `.es6` and `.js` (atScript)
// files in the project and the TypeScript compiler only looks for `.ts` files when trying
// to load imports.
extensions: ['.ts', '.js'],
// The options for the TS compiler
options: {
allowNonTsExtensions: true,
charset: 'utf8'
},
parse: function(fileNames, baseDir) {
// "Compile" a program from the given module filenames, to get hold of a
// typeChecker that can be used to interrogate the modules, exports and so on.
var host = createCompilerHost(this.options, baseDir, this.extensions);
var program = ts.createProgram(fileNames, this.options, host);
var typeChecker = program.getTypeChecker();
// Create an array of module symbols for each file we were given
var moduleSymbols = [];
fileNames.forEach(function(fileName) {
var sourceFile = program.getSourceFile(fileName);
if (!sourceFile) {
throw new Error('Invalid source file: ' + fileName);
} else if (!sourceFile.symbol) {
// Some files contain only a comment and no actual module code
log.warn('No module code found in ' + fileName);
} else {
moduleSymbols.push(sourceFile.symbol);
}
});
moduleSymbols.forEach(function(tsModule) {
// The type checker has a nice helper function that returns an array of Symbols
// representing the exports for a given module
tsModule.exportArray = typeChecker.getExportsOfModule(tsModule);
// Although 'star' imports (e.g. `export * from 'some/module';) get resolved automatically
// by the compiler/binder, it seems that explicit imports (e.g. `export {SomeClass} from 'some/module'`)
// do not so we have to do a little work.
tsModule.exportArray.forEach(function(moduleExport) {
if (moduleExport.flags & ts.SymbolFlags.Alias) {
// To maintain the alias information (particularly the alias name)
// we just attach the original "resolved" symbol to the alias symbol
moduleExport.resolvedSymbol = typeChecker.getAliasedSymbol(moduleExport);
}
});
});
moduleSymbols.typeChecker = typeChecker;
return {
moduleSymbols: moduleSymbols,
typeChecker: typeChecker,
program: program,
host: host
};
}
};
};

View File

@ -1,21 +0,0 @@
var mockPackage = require('../../mocks/mockPackage');
var Dgeni = require('dgeni');
var path = require('canonical-path');
describe('tsParser', function() {
var dgeni, injector, parser;
beforeEach(function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
parser = injector.get('tsParser');
});
it("should parse a TS file", function() {
var parseInfo = parser.parse(['testSrc.ts'], path.resolve(__dirname, '../../mocks/tsParser'));
var tsModules = parseInfo.moduleSymbols;
expect(tsModules.length).toEqual(1);
expect(tsModules[0].exportArray.length).toEqual(3);
expect(tsModules[0].exportArray.map(function(i) { return i.name; })).toEqual(['MyClass', 'myFn', 'x']);
});
});

File diff suppressed because it is too large Load Diff

View File

@ -1,86 +0,0 @@
// This module provides a customFileHandler for karma
// that serves files with urls like /packages_<timestamp>/...
// with maximum cache.
// We are using these urls when we spawn isolates
// so that the isolates don't reload files every time.
var common = require('karma/lib/middleware/common');
var fs = require('fs');
var DART_EVAL_PATH_RE = /.*\/packages_\d+\/(.*)$/;
module.exports = createFactory;
function createFactory(proxyPaths) {
return {
'framework:dart-evalcache': ['factory', dartEvalCacheFactory]
};
function dartEvalCacheFactory(emitter, logger, customFileHandlers) {
var filesPromise = new common.PromiseContainer();
emitter.on('file_list_modified', function(files) {
filesPromise.set(Promise.resolve(files));
});
var serveFile = common.createServeFile(fs);
var log = logger.create('dart-evalcache');
customFileHandlers.push({
urlRegex: DART_EVAL_PATH_RE,
handler: handler
});
// See source_files handler
function handler(request, response, fa, fb, basePath) {
return filesPromise.then(function(files) {
try {
var requestedFilePath = mapUrlToFile(request.url, proxyPaths, basePath, log);
// TODO(vojta): change served to be a map rather then an array
var file = findByPath(files.served, requestedFilePath);
if (file) {
serveFile(file.contentPath || file.path, response, function() {
common.setHeavyCacheHeaders(response);
}, file.content);
} else {
response.writeHead(404);
response.end('Not found');
}
} catch (e) {
log.error(e.stack);
response.writeHead(500);
response.end('Error', e.stack);
}
});
}
};
}
function mapUrlToFile(url, proxyPaths, basePath, log) {
var originalUrl = url;
url = url.indexOf('?') > -1 ? url.substring(0, url.indexOf('?')) : url;
var match = DART_EVAL_PATH_RE.exec(url);
var packagePath = match[1];
var result = null;
var lastProxyFromLength = 0;
Object.keys(proxyPaths).forEach(function(proxyFrom) {
if (startsWith(packagePath, proxyFrom) && proxyFrom.length > lastProxyFromLength) {
lastProxyFromLength = proxyFrom.length;
result = proxyPaths[proxyFrom] + packagePath.substring(proxyFrom.length);
}
});
return basePath + '/' + result;
}
function startsWith(string, subString) {
return string.length >= subString.length && string.slice(0, subString.length) === subString;
}
function findByPath(files, path) {
for (var i = 0; i < files.length; i++) {
if (files[i].path === path) {
return files[i];
}
}
return null;
}

View File

@ -1,82 +1,93 @@
var sauceConf = require('./sauce.conf');
var packageSources = {
// Dependencies installed with `pub install`.
'unittest': 'packages/unittest',
'guinness': 'packages/guinness',
'matcher': 'packages/matcher',
'stack_trace': 'packages/stack_trace',
'collection': 'packages/collection',
'path': 'packages/path',
'observe': 'packages/observe',
'quiver': 'packages/quiver',
'intl': 'packages/intl',
'smoke': 'packages/smoke',
'logging': 'packages/logging',
'utf': 'packages/utf',
// Local dependencies, transpiled from the source.
'angular2': 'dist/dart/angular2/lib',
'angular2/test/': 'dist/dart/angular2/test/',
'http': 'dist/dart/http/lib',
'angular2_material': 'dist/dart/angular2_material/lib',
'benchpress': 'dist/dart/benchpress/lib',
'examples': 'dist/dart/examples/lib'
};
var proxyPaths = {};
Object.keys(packageSources).map(function(packageName) {
var filePath = packageSources[packageName];
proxyPaths['/packages/'+packageName] = '/base/'+filePath;
});
// Karma configuration
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
var file2moduleName = require('./tools/build/file2modulename');
module.exports = function(config) {
config.set({
frameworks: ['dart-unittest', 'dart-evalcache'],
frameworks: ['dart-unittest'],
files: [
// Init and configure guiness.
{pattern: 'test-init.dart', included: true},
// Unit test files needs to be included.
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: false},
// Karma-dart generates `__adapter_unittest.dart` that imports these files.
{pattern: 'modules/*/test/**/*_spec.js', included: true},
{pattern: 'tools/transpiler/spec/**/*_spec.js', included: true},
// Karma-dart via the dart-unittest framework generates
// `__adapter_unittest.dart` that imports these files.
{pattern: 'dist/dart/**', included: false, watched: false},
// These files are not included, they are imported by the unit tests above.
{pattern: 'modules/**', included: false},
{pattern: 'tools/transpiler/spec/**/*', included: false},
// Dependencies, installed with `pub install`.
{pattern: 'packages/**/*.dart', included: false, watched: false},
// Init and configure guiness.
{pattern: 'test-main.dart', included: true},
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false},
],
exclude: [
'dist/dart/**/packages/**',
'modules/angular1_router/**'
{pattern: 'test-main.dart', included: true}
],
karmaDartImports: {
guinness: 'package:guinness/guinness_html.dart'
},
// TODO(vojta): Remove the localhost:9877 from urls, once the proxy fix is merged:
// https://github.com/karma-runner/karma/pull/1207
//
// Map packages to the correct urls where Karma serves them.
proxies: proxyPaths,
proxies: {
// Dependencies installed with `pub install`.
'/packages/unittest': 'http://localhost:9877/base/packages/unittest',
'/packages/guinness': 'http://localhost:9877/base/packages/guinness',
'/packages/matcher': 'http://localhost:9877/base/packages/matcher',
'/packages/stack_trace': 'http://localhost:9877/base/packages/stack_trace',
'/packages/collection': 'http://localhost:9877/base/packages/collection',
'/packages/path': 'http://localhost:9877/base/packages/path',
customLaunchers: sauceConf.customLaunchers,
// Local dependencies, transpiled from the source.
'/packages/angular': 'http://localhost:9877/base/modules/angular',
'/packages/benchpress': 'http://localhost:9877/base/modules/benchpress',
'/packages/core': 'http://localhost:9877/base/modules/core',
'/packages/change_detection': 'http://localhost:9877/base/modules/change_detection',
'/packages/reflection': 'http://localhost:9877/base/modules/reflection',
'/packages/di': 'http://localhost:9877/base/modules/di',
'/packages/directives': 'http://localhost:9877/base/modules/directives',
'/packages/facade': 'http://localhost:9877/base/modules/facade',
'/packages/forms': 'http://localhost:9877/base/modules/forms',
'/packages/test_lib': 'http://localhost:9877/base/modules/test_lib',
'/packages/mock': 'http://localhost:9877/base/modules/mock',
},
preprocessors: {
'modules/**/*.js': ['traceur'],
'tools/**/*.js': ['traceur']
},
traceurPreprocessor: {
options: {
outputLanguage: 'dart',
sourceMaps: true,
script: false,
modules: 'register',
memberVariables: true,
types: true,
// typeAssertions: true,
// typeAssertionModule: 'assert',
annotations: true
},
resolveModuleName: file2moduleName,
transformPath: function(fileName) {
return fileName.replace('.js', '.dart');
}
},
customLaunchers: {
DartiumWithWebPlatform: {
base: 'Dartium',
flags: ['--enable-experimental-web-platform-features'] }
},
browsers: ['DartiumWithWebPlatform'],
port: 9877,
plugins: [
require('karma-dart'),
require('karma-chrome-launcher'),
require('karma-sauce-launcher'),
require('./karma-dart-evalcache')(packageSources)
]
port: 9877
});
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
};

View File

@ -1,7 +1,7 @@
var sauceConf = require('./sauce.conf');
// Karma configuration
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
var file2moduleName = require('./tools/build/file2modulename');
module.exports = function(config) {
config.set({
@ -9,57 +9,60 @@ module.exports = function(config) {
files: [
// Sources and specs.
// Loaded through the System loader, in `test-main.js`.
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
'node_modules/es6-shim/es6-shim.js',
// include Angular v1 for upgrade module testing
'node_modules/angular/angular.min.js',
// zone-microtask must be included first as it contains a Promise monkey patch
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/jasmine-patch.js',
// Loaded through the es6-module-loader, in `test-main.js`.
{pattern: 'modules/**', included: false},
{pattern: 'tools/transpiler/spec/**', included: false},
'node_modules/traceur/bin/traceur-runtime.js',
'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
// Including systemjs because it defines `__eval`, which produces correct stack traces.
'modules/angular2/src/testing/shims_for_IE.js',
'node_modules/systemjs/dist/system.src.js',
{pattern: 'node_modules/@reactivex/rxjs/**', included: false, watched: false, served: true},
'node_modules/reflect-metadata/Reflect.js',
'node_modules/systemjs/lib/extension-register.js',
'node_modules/zone.js/zone.js',
'node_modules/zone.js/long-stack-trace-zone.js',
'tools/build/file2modulename.js',
'test-main.js',
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
'test-main.js'
],
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/angular1_router.js'],
exclude: [
'modules/**/e2e_test/**'
],
customLaunchers: sauceConf.customLaunchers,
preprocessors: {
'modules/**/*.js': ['traceur'],
'modules/**/*.es6': ['traceur'],
'tools/transpiler/spec/**/*.js': ['traceur'],
'tools/transpiler/spec/**/*.es6': ['traceur'],
},
sauceLabs: {
testName: 'Angular2',
startConnect: false,
recordVideo: false,
recordScreenshots: false,
traceurPreprocessor: {
options: {
'selenium-version': '2.47.1',
'command-timeout': 600,
'idle-timeout': 600,
'max-duration': 5400
outputLanguage: 'es5',
sourceMaps: true,
script: false,
memberVariables: true,
modules: 'instantiate',
types: true,
typeAssertions: true,
typeAssertionModule: 'rtts_assert/rtts_assert',
annotations: true
},
resolveModuleName: file2moduleName,
transformPath: function(fileName) {
return fileName.replace(/\.es6$/, '.js');
}
},
browsers: ['Chrome'],
customLaunchers: {
DartiumWithWebPlatform: {
base: 'Dartium',
flags: ['--enable-experimental-web-platform-features'] }
},
browsers: ['ChromeCanary'],
port: 9876
});
if (process.env.TRAVIS && process.env.MODE === 'saucelabs') {
config.sauceLabs.build = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
// TODO(mlaval): remove once SauceLabs supports websockets.
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
console.log('>>>> setting socket.io transport to polling <<<<');
config.transports = ['polling'];
}
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
};

1
karma.conf.js Symbolic link
View File

@ -0,0 +1 @@
karma-js.conf.js

View File

@ -1,67 +0,0 @@
'use strict';
var fs = require('fs');
var ts = require('typescript');
var files = [
'lifecycle_annotations_impl.ts',
'url_parser.ts',
'path_recognizer.ts',
'route_config_impl.ts',
'async_route_handler.ts',
'sync_route_handler.ts',
'route_recognizer.ts',
'instruction.ts',
'route_config_nomalizer.ts',
'route_lifecycle_reflector.ts',
'route_registry.ts',
'router.ts'
];
var PRELUDE = '(function(){\n';
var POSTLUDE = '\n}());\n';
var FACADES = fs.readFileSync(__dirname + '/lib/facades.es5', 'utf8');
var DIRECTIVES = fs.readFileSync(__dirname + '/src/ng_outlet.ts', 'utf8');
var moduleTemplate = fs.readFileSync(__dirname + '/src/module_template.js', 'utf8');
function main() {
var dir = __dirname + '/../angular2/src/router/';
var sharedCode = files.reduce(function (prev, file) {
return prev + transform(fs.readFileSync(dir + file, 'utf8'));
}, '');
var out = moduleTemplate.replace('//{{FACADES}}', FACADES).replace('//{{SHARED_CODE}}', sharedCode);
return PRELUDE + transform(DIRECTIVES) + out + POSTLUDE;
}
/*
* Given a directory name and a file's TypeScript content, return an object with the ES5 code,
* sourcemap, and exported variable identifier name for the content.
*/
var IMPORT_RE = new RegExp("import \\{?([\\w\\n_, ]+)\\}? from '(.+)';?", 'g');
function transform(contents) {
contents = contents.replace(IMPORT_RE, function (match, imports, includePath) {
//TODO: remove special-case
if (isFacadeModule(includePath) || includePath === './router_outlet') {
return '';
}
return match;
});
return ts.transpile(contents, {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS
});
}
function isFacadeModule(modulePath) {
return modulePath.indexOf('facade') > -1 ||
modulePath === 'angular2/src/core/reflection/reflection';
}
module.exports = function () {
var dist = __dirname + '/../../dist';
if (!fs.existsSync(dist)) {
fs.mkdirSync(dist);
}
fs.writeFileSync(dist + '/angular_1_router.js', main());
};

View File

@ -1,22 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div ng-app="myApp" ng-controller="MyCtrl">
</div>
<script src="../../node_modules/angular/angular.js"></script>
<script src="../../dist/angular_1_router.js"></script>
<script>
angular.module('myApp', ['ngComponentRouter'])
.controller('MyCtrl', ['$router', function ($router) {
console.log($router);
$router.navigateByUrl('/')
.then(console.log.bind(console, 'resolve'), console.log.bind(console, 'reject'));
}]);
</script>
</body>
</html>

View File

@ -1,29 +0,0 @@
'use strict';
var sauceConf = require('../../sauce.conf');
// This runs the tests for the router in Angular 1.x
module.exports = function (config) {
var options = {
frameworks: ['jasmine'],
files: [
'../../node_modules/angular/angular.js',
'../../node_modules/angular-animate/angular-animate.js',
'../../node_modules/angular-mocks/angular-mocks.js',
'../../dist/angular_1_router.js',
'src/ng_route_shim.js',
'test/*.es5.js',
'test/**/*_spec.js'
],
customLaunchers: sauceConf.customLaunchers,
browsers: ['ChromeCanary']
};
config.set(options);
};

View File

@ -1,307 +0,0 @@
function CONST() {
return (function(target) {
return target;
});
}
function CONST_EXPR(expr) {
return expr;
}
function isPresent (x) {
return !!x;
}
function isBlank (x) {
return !x;
}
function isString(obj) {
return typeof obj === 'string';
}
function isType (x) {
return typeof x === 'function';
}
function isStringMap(obj) {
return typeof obj === 'object' && obj !== null;
}
function isArray(obj) {
return Array.isArray(obj);
}
function getTypeNameForDebugging (fn) {
return fn.name || 'Root';
}
var PromiseWrapper = {
resolve: function (reason) {
return $q.when(reason);
},
reject: function (reason) {
return $q.reject(reason);
},
catchError: function (promise, fn) {
return promise.then(null, fn);
},
all: function (promises) {
return $q.all(promises);
}
};
var RegExpWrapper = {
create: function(regExpStr, flags) {
flags = flags ? flags.replace(/g/g, '') : '';
return new RegExp(regExpStr, flags + 'g');
},
firstMatch: function(regExp, input) {
regExp.lastIndex = 0;
return regExp.exec(input);
},
matcher: function (regExp, input) {
regExp.lastIndex = 0;
return { re: regExp, input: input };
}
};
var reflector = {
annotations: function (fn) {
//TODO: implement me
return fn.annotations || [];
}
};
var MapWrapper = {
create: function() {
return new Map();
},
get: function(m, k) {
return m.get(k);
},
set: function(m, k, v) {
return m.set(k, v);
},
contains: function (m, k) {
return m.has(k);
},
forEach: function (m, fn) {
return m.forEach(fn);
}
};
var StringMapWrapper = {
create: function () {
return {};
},
set: function (m, k, v) {
return m[k] = v;
},
get: function (m, k) {
return m.hasOwnProperty(k) ? m[k] : undefined;
},
contains: function (m, k) {
return m.hasOwnProperty(k);
},
keys: function(map) {
return Object.keys(map);
},
isEmpty: function(map) {
for (var prop in map) {
if (map.hasOwnProperty(prop)) {
return false;
}
}
return true;
},
delete: function(map, key) {
delete map[key];
},
forEach: function (m, fn) {
for (var prop in m) {
if (m.hasOwnProperty(prop)) {
fn(m[prop], prop);
}
}
},
equals: function (m1, m2) {
var k1 = Object.keys(m1);
var k2 = Object.keys(m2);
if (k1.length != k2.length) {
return false;
}
var key;
for (var i = 0; i < k1.length; i++) {
key = k1[i];
if (m1[key] !== m2[key]) {
return false;
}
}
return true;
},
merge: function(m1, m2) {
var m = {};
for (var attr in m1) {
if (m1.hasOwnProperty(attr)) {
m[attr] = m1[attr];
}
}
for (var attr in m2) {
if (m2.hasOwnProperty(attr)) {
m[attr] = m2[attr];
}
}
return m;
}
};
var List = Array;
var ListWrapper = {
create: function () {
return [];
},
push: function (l, v) {
return l.push(v);
},
forEach: function (l, fn) {
return l.forEach(fn);
},
first: function(array) {
if (!array)
return null;
return array[0];
},
map: function (l, fn) {
return l.map(fn);
},
join: function (l, str) {
return l.join(str);
},
reduce: function(list, fn, init) {
return list.reduce(fn, init);
},
filter: function(array, pred) {
return array.filter(pred);
},
concat: function(a, b) {
return a.concat(b);
},
slice: function(l) {
var from = arguments[1] !== (void 0) ? arguments[1] : 0;
var to = arguments[2] !== (void 0) ? arguments[2] : null;
return l.slice(from, to === null ? undefined : to);
},
maximum: function(list, predicate) {
if (list.length == 0) {
return null;
}
var solution = null;
var maxValue = -Infinity;
for (var index = 0; index < list.length; index++) {
var candidate = list[index];
if (isBlank(candidate)) {
continue;
}
var candidateValue = predicate(candidate);
if (candidateValue > maxValue) {
solution = candidate;
maxValue = candidateValue;
}
}
return solution;
}
};
var StringWrapper = {
equals: function (s1, s2) {
return s1 === s2;
},
split: function(s, re) {
return s.split(re);
},
substring: function(s, start, end) {
return s.substr(start, end);
},
replaceAll: function(s, from, replace) {
return s.replace(from, replace);
},
startsWith: function(s, start) {
return s.substr(0, start.length) === start;
},
replaceAllMapped: function(s, from, cb) {
return s.replace(from, function(matches) {
// Remove offset & string from the result array
matches.splice(-2, 2);
// The callback receives match, p1, ..., pn
return cb.apply(null, matches);
});
},
contains: function(s, substr) {
return s.indexOf(substr) != -1;
}
};
//TODO: implement?
// I think it's too heavy to ask 1.x users to bring in Rx for the router...
function EventEmitter() {}
var BaseException = Error;
var ObservableWrapper = {
callNext: function(ob, val) {
ob.fn(val);
},
subscribe: function(ob, fn) {
ob.fn = fn;
}
};
// TODO: https://github.com/angular/angular.js/blob/master/src/ng/browser.js#L227-L265
var $__router_47_location__ = {
Location: Location
};
function Location(){}
Location.prototype.subscribe = function () {
//TODO: implement
};
Location.prototype.path = function () {
return $location.path();
};
Location.prototype.go = function (url) {
return $location.path(url);
};

View File

@ -1,66 +0,0 @@
angular.module('ngComponentRouter').
value('$route', null). // can be overloaded with ngRouteShim
factory('$router', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', routerFactory]);
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector) {
// When this file is processed, the line below is replaced with
// the contents of `../lib/facades.es5`.
//{{FACADES}}
var exports = {Injectable: function () {}};
var require = function () {return exports;};
// When this file is processed, the line below is replaced with
// the contents of the compiled TypeScript classes.
//{{SHARED_CODE}}
//TODO: this is a hack to replace the exiting implementation at run-time
exports.getCanActivateHook = function (directiveName) {
var factory = $$directiveIntrospector.getTypeByName(directiveName);
return factory && factory.$canActivate && function (next, prev) {
return $injector.invoke(factory.$canActivate, null, {
$nextInstruction: next,
$prevInstruction: prev
});
};
};
// This hack removes assertions about the type of the "component"
// property in a route config
exports.assertComponentExists = function () {};
angular.stringifyInstruction = exports.stringifyInstruction;
var RouteRegistry = exports.RouteRegistry;
var RootRouter = exports.RootRouter;
var registry = new RouteRegistry();
var location = new Location();
$$directiveIntrospector(function (name, factory) {
if (angular.isArray(factory.$routeConfig)) {
factory.$routeConfig.forEach(function (config) {
registry.config(name, config);
});
}
});
// Because Angular 1 has no notion of a root component, we use an object with unique identity
// to represent this.
var ROOT_COMPONENT_OBJECT = new Object();
var router = new RootRouter(registry, location, ROOT_COMPONENT_OBJECT);
$rootScope.$watch(function () { return $location.path(); }, function (path) {
if (router.lastNavigationAttempt !== path) {
router.navigateByUrl(path);
}
});
router.subscribe(function () {
$rootScope.$broadcast('$routeChangeSuccess', {});
});
return router;
}

View File

@ -1,297 +0,0 @@
///<reference path="../typings/angularjs/angular.d.ts"/>
/*
* decorates $compileProvider so that we have access to routing metadata
*/
function compilerProviderDecorator($compileProvider,
$$directiveIntrospectorProvider: DirectiveIntrospectorProvider) {
let directive = $compileProvider.directive;
$compileProvider.directive = function(name: string, factory: Function) {
$$directiveIntrospectorProvider.register(name, factory);
return directive.apply(this, arguments);
};
}
/*
* private service that holds route mappings for each controller
*/
class DirectiveIntrospectorProvider {
private directiveBuffer: any[] = [];
private directiveFactoriesByName: {[name: string]: Function} = {};
private onDirectiveRegistered: (name: string, factory: Function) => any = null;
register(name: string, factory: Function) {
if (angular.isArray(factory)) {
factory = factory[factory.length - 1];
}
this.directiveFactoriesByName[name] = factory;
if (this.onDirectiveRegistered) {
this.onDirectiveRegistered(name, factory);
} else {
this.directiveBuffer.push({name: name, factory: factory});
}
}
$get() {
let fn: any = newOnControllerRegistered => {
this.onDirectiveRegistered = newOnControllerRegistered;
while (this.directiveBuffer.length > 0) {
let directive = this.directiveBuffer.pop();
this.onDirectiveRegistered(directive.name, directive.factory);
}
};
fn.getTypeByName = name => this.directiveFactoriesByName[name];
return fn;
}
}
/**
* @name ngOutlet
*
* @description
* An ngOutlet is where resolved content goes.
*
* ## Use
*
* ```html
* <div ng-outlet="name"></div>
* ```
*
* The value for the `ngOutlet` attribute is optional.
*/
function ngOutletDirective($animate, $q: ng.IQService, $router) {
let rootRouter = $router;
return {
restrict: 'AE',
transclude: 'element',
terminal: true,
priority: 400,
require: ['?^^ngOutlet', 'ngOutlet'],
link: outletLink,
controller: class {},
controllerAs: '$$ngOutlet'
};
function outletLink(scope, element, attrs, ctrls, $transclude) {
class Outlet {
constructor(private controller, private router) {}
private currentController;
private currentInstruction;
private currentScope;
private currentElement;
private previousLeaveAnimation;
private cleanupLastView() {
if (this.previousLeaveAnimation) {
$animate.cancel(this.previousLeaveAnimation);
this.previousLeaveAnimation = null;
}
if (this.currentScope) {
this.currentScope.$destroy();
this.currentScope = null;
}
if (this.currentElement) {
this.previousLeaveAnimation = $animate.leave(this.currentElement);
this.previousLeaveAnimation.then(() => this.previousLeaveAnimation = null);
this.currentElement = null;
}
}
reuse(instruction) {
let next = $q.when(true);
let previousInstruction = this.currentInstruction;
this.currentInstruction = instruction;
if (this.currentController && this.currentController.$onReuse) {
next = $q.when(
this.currentController.$onReuse(this.currentInstruction, previousInstruction));
}
return next;
}
canReuse(nextInstruction) {
let result;
if (!this.currentInstruction ||
this.currentInstruction.componentType !== nextInstruction.componentType) {
result = false;
} else if (this.currentController && this.currentController.$canReuse) {
result = this.currentController.$canReuse(nextInstruction, this.currentInstruction);
} else {
result = nextInstruction === this.currentInstruction ||
angular.equals(nextInstruction.params, this.currentInstruction.params);
}
return $q.when(result);
}
canDeactivate(instruction) {
if (this.currentController && this.currentController.$canDeactivate) {
return $q.when(
this.currentController.$canDeactivate(instruction, this.currentInstruction));
}
return $q.when(true);
}
deactivate(instruction) {
if (this.currentController && this.currentController.$onDeactivate) {
return $q.when(
this.currentController.$onDeactivate(instruction, this.currentInstruction));
}
return $q.when();
}
activate(instruction) {
let previousInstruction = this.currentInstruction;
this.currentInstruction = instruction;
let componentName = this.controller.$$componentName = instruction.componentType;
if (typeof componentName !== 'string') {
throw new Error('Component is not a string for ' + instruction.urlPath);
}
this.controller.$$routeParams = instruction.params;
this.controller.$$template = '<div ' + dashCase(componentName) + '></div>';
this.controller.$$router = this.router.childRouter(instruction.componentType);
let newScope = scope.$new();
let clone = $transclude(newScope, clone => {
$animate.enter(clone, null, this.currentElement || element);
this.cleanupLastView();
});
this.currentElement = clone;
this.currentScope = newScope;
// TODO: prefer the other directive retrieving the controller
// by debug mode
this.currentController = this.currentElement.children().eq(0).controller(componentName);
if (this.currentController && this.currentController.$onActivate) {
return this.currentController.$onActivate(instruction, previousInstruction);
}
return $q.when();
}
}
let parentCtrl = ctrls[0], myCtrl = ctrls[1],
router = (parentCtrl && parentCtrl.$$router) || rootRouter;
myCtrl.$$currentComponent = null;
router.registerPrimaryOutlet(new Outlet(myCtrl, router));
}
}
/**
* This directive is responsible for compiling the contents of ng-outlet
*/
function ngOutletFillContentDirective($compile) {
return {
restrict: 'EA',
priority: -400,
require: 'ngOutlet',
link: (scope, element, attrs, ctrl) => {
let template = ctrl.$$template;
element.html(template);
let link = $compile(element.contents());
link(scope);
// TODO: move to primary directive
let componentInstance = scope[ctrl.$$componentName];
if (componentInstance) {
ctrl.$$currentComponent = componentInstance;
componentInstance.$router = ctrl.$$router;
componentInstance.$routeParams = ctrl.$$routeParams;
}
}
};
}
/**
* @name ngLink
* @description
* Lets you link to different parts of the app, and automatically generates hrefs.
*
* ## Use
* The directive uses a simple syntax: `ng-link="componentName({ param: paramValue })"`
*
* ## Example
*
* ```js
* angular.module('myApp', ['ngComponentRouter'])
* .controller('AppController', ['$router', function($router) {
* $router.config({ path: '/user/:id', component: 'user' });
* this.user = { name: 'Brian', id: 123 };
* });
* ```
*
* ```html
* <div ng-controller="AppController as app">
* <a ng-link="user({id: app.user.id})">{{app.user.name}}</a>
* </div>
* ```
*/
function ngLinkDirective($router, $parse) {
let rootRouter = $router;
return {require: '?^^ngOutlet', restrict: 'A', link: ngLinkDirectiveLinkFn};
function ngLinkDirectiveLinkFn(scope, element, attrs, ctrl) {
let router = (ctrl && ctrl.$$router) || rootRouter;
if (!router) {
return;
}
let instruction = null;
let link = attrs.ngLink || '';
function getLink(params) {
instruction = router.generate(params);
return './' + angular.stringifyInstruction(instruction);
}
let routeParamsGetter = $parse(link);
// we can avoid adding a watcher if it's a literal
if (routeParamsGetter.constant) {
let params = routeParamsGetter();
element.attr('href', getLink(params));
} else {
scope.$watch(() => routeParamsGetter(scope), params => element.attr('href', getLink(params)),
true);
}
element.on('click', event => {
if (event.which !== 1 || !instruction) {
return;
}
$router.navigateByInstruction(instruction);
event.preventDefault();
});
}
}
function dashCase(str: string): string {
return str.replace(/[A-Z]/g, match => '-' + match.toLowerCase());
}
/*
* A module for adding new a routing system Angular 1.
*/
angular.module('ngComponentRouter', [])
.directive('ngOutlet', ngOutletDirective)
.directive('ngOutlet', ngOutletFillContentDirective)
.directive('ngLink', ngLinkDirective);
/*
* A module for inspecting controller constructors
*/
angular.module('ng')
.provider('$$directiveIntrospector', DirectiveIntrospectorProvider)
.config(compilerProviderDecorator);

View File

@ -1,349 +0,0 @@
/** @license Copyright 2014-2015 Google, Inc. http://github.com/angular/angular/LICENSE */
(function () {
'use strict';
// keep a reference to compileProvider so we can register new component-directives
// on-the-fly based on $routeProvider configuration
// TODO: remove this right now you can only bootstrap one Angular app with this hack
var $compileProvider, $q, $injector;
/**
* This module loads services that mimic ngRoute's configuration, and includes
* an anchor link directive that intercepts clicks to routing.
*
* This module is intended to be used as a stop-gap solution for projects upgrading from ngRoute.
* It intentionally does not implement all features of ngRoute.
*/
angular.module('ngRouteShim', [])
.provider('$route', $RouteProvider)
.config(['$compileProvider', function (compileProvider) {
$compileProvider = compileProvider;
}])
.factory('$routeParams', $routeParamsFactory)
.directive('a', anchorLinkDirective)
// Connects the legacy $routeProvider config shim to Component Router's config.
.run(['$route', '$router', function ($route, $router) {
$route.$$subscribe(function (routeDefinition) {
if (!angular.isArray(routeDefinition)) {
routeDefinition = [routeDefinition];
}
$router.config(routeDefinition);
});
}]);
/**
* A shimmed out provider that provides the same API as ngRoute's $routeProvider, but uses these calls
* to configure Component Router.
*/
function $RouteProvider() {
var routes = [];
var subscriptionFn = null;
var routeMap = {};
// Stats for which routes are skipped
var skipCount = 0;
var successCount = 0;
var allCount = 0;
function consoleMetrics() {
return '(' + skipCount + ' skipped / ' + successCount + ' success / ' + allCount + ' total)';
}
/**
* @ngdoc method
* @name $routeProvider#when
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
*/
this.when = function(path, route) {
//copy original route object to preserve params inherited from proto chain
var routeCopy = angular.copy(route);
allCount++;
if (angular.isDefined(routeCopy.reloadOnSearch)) {
console.warn('Route for "' + path + '" uses "reloadOnSearch" which is not implemented.');
}
if (angular.isDefined(routeCopy.caseInsensitiveMatch)) {
console.warn('Route for "' + path + '" uses "caseInsensitiveMatch" which is not implemented.');
}
// use new wildcard format
path = reformatWildcardParams(path);
if (path[path.length - 1] == '*') {
skipCount++;
console.warn('Route for "' + path + '" ignored because it ends with *. Skipping.', consoleMetrics());
return this;
}
if (path.indexOf('?') > -1) {
skipCount++;
console.warn('Route for "' + path + '" ignored because it has optional parameters. Skipping.', consoleMetrics());
return this;
}
if (typeof route.redirectTo == 'function') {
skipCount++;
console.warn('Route for "' + path + '" ignored because lazy redirecting to a function is not yet implemented. Skipping.', consoleMetrics());
return this;
}
var routeDefinition = {
path: path,
data: routeCopy
};
routeMap[path] = routeCopy;
if (route.redirectTo) {
routeDefinition.redirectTo = route.redirectTo;
} else {
if (routeCopy.controller && !routeCopy.controllerAs) {
console.warn('Route for "' + path + '" should use "controllerAs".');
}
var directiveName = routeObjToRouteName(routeCopy, path);
if (!directiveName) {
throw new Error('Could not determine a name for route "' + path + '".');
}
routeDefinition.component = directiveName;
routeDefinition.as = upperCase(directiveName);
var directiveController = routeCopy.controller;
var directiveDefinition = {
scope: false,
controller: directiveController,
controllerAs: routeCopy.controllerAs,
templateUrl: routeCopy.templateUrl,
template: routeCopy.template
};
var directiveFactory = function () {
return directiveDefinition;
};
// if we have route resolve options, prepare a wrapper controller
if (directiveController && routeCopy.resolve) {
var originalController = directiveController;
var resolvedLocals = {};
directiveDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
var locals = angular.extend({
$scope: $scope
}, resolvedLocals);
var ctrl = $injector.instantiate(originalController, locals);
if (routeCopy.controllerAs) {
locals.$scope[routeCopy.controllerAs] = ctrl;
}
return ctrl;
}];
// we take care of controllerAs in the directive controller wrapper
delete directiveDefinition.controllerAs;
// we resolve the locals in a canActivate block
directiveFactory.$canActivate = function() {
var locals = angular.extend({}, routeCopy.resolve);
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value, null, null, key);
});
return $q.all(locals).then(function (locals) {
resolvedLocals = locals;
}).then(function () {
return true;
});
};
}
// register the dynamically created directive
$compileProvider.directive(directiveName, directiveFactory);
}
if (subscriptionFn) {
subscriptionFn(routeDefinition);
} else {
routes.push(routeDefinition);
}
successCount++;
return this;
};
this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when('/*rest', params);
return this;
};
this.$get = ['$q', '$injector', function (q, injector) {
$q = q;
$injector = injector;
var $route = {
routes: routeMap,
/**
* @ngdoc method
* @name $route#reload
*
* @description
* Causes `$route` service to reload the current route even if
* {@link ng.$location $location} hasn't changed.
*/
reload: function() {
throw new Error('Not implemented: $route.reload');
},
/**
* @ngdoc method
* @name $route#updateParams
*/
updateParams: function(newParams) {
throw new Error('Not implemented: $route.updateParams');
},
/**
* Runs the given `fn` whenever new configs are added.
* Only one subscription is allowed.
* Passed `fn` is called synchronously.
*/
$$subscribe: function(fn) {
if (subscriptionFn) {
throw new Error('only one subscription allowed');
}
subscriptionFn = fn;
subscriptionFn(routes);
routes = [];
},
/**
* Runs a string with stats about many route configs were adapted, and how many were
* dropped because they are incompatible.
*/
$$getStats: consoleMetrics
};
return $route;
}];
}
function $routeParamsFactory($router, $rootScope) {
// the identity of this object cannot change
var paramsObj = {};
$rootScope.$on('$routeChangeSuccess', function () {
var newParams = $router._currentInstruction && $router._currentInstruction.component.params;
angular.forEach(paramsObj, function (val, name) {
delete paramsObj[name];
});
angular.forEach(newParams, function (val, name) {
paramsObj[name] = val;
});
});
return paramsObj;
}
/**
* Allows normal anchor links to kick off routing.
*/
function anchorLinkDirective($router) {
return {
restrict: 'E',
link: function (scope, element) {
// If the linked element is not an anchor tag anymore, do nothing
if (element[0].nodeName.toLowerCase() !== 'a') {
return;
}
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
var hrefAttrName = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
'xlink:href' : 'href';
element.on('click', function (event) {
if (event.which !== 1) {
return;
}
var href = element.attr(hrefAttrName);
if (href && $router.recognize(href)) {
$router.navigateByUrl(href);
event.preventDefault();
}
});
}
};
}
/**
* Given a route object, attempts to find a unique directive name.
*
* @param route route config object passed to $routeProvider.when
* @param path route configuration path
* @returns {string|name} a normalized (camelCase) directive name
*/
function routeObjToRouteName(route, path) {
var name = route.controllerAs;
var controller = route.controller;
if (!name && controller) {
if (angular.isArray(controller)) {
controller = controller[controller.length - 1];
}
name = controller.name;
}
if (!name) {
var segments = path.split('/');
name = segments[segments.length - 1];
}
if (name) {
name = name + 'AutoCmp';
}
return name;
}
function upperCase(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
}
/*
* Changes "/:foo*" to "/*foo"
*/
var WILDCARD_PARAM_RE = new RegExp('\\/:([a-z0-9]+)\\*', 'gi');
function reformatWildcardParams(path) {
return path.replace(WILDCARD_PARAM_RE, function (m, m1) {
return '/*' + m1;
});
}
}());

View File

@ -1,38 +0,0 @@
'use strict';
describe('$$directiveIntrospector', function () {
var $compileProvider;
beforeEach(function() {
module('ng');
module('ngComponentRouter');
module(function(_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
});
it('should call the introspector function whenever a directive factory is registered', inject(function ($$directiveIntrospector) {
var spy = jasmine.createSpy();
$$directiveIntrospector(spy);
function myDir(){}
$compileProvider.directive('myDir', myDir);
expect(spy).toHaveBeenCalledWith('myDir', myDir);
}));
it('should call the introspector function whenever a directive factory is registered with array annotations', inject(function ($$directiveIntrospector) {
var spy = jasmine.createSpy();
$$directiveIntrospector(spy);
function myDir(){}
$compileProvider.directive('myDir', ['foo', myDir]);
expect(spy).toHaveBeenCalledWith('myDir', myDir);
}));
it('should retrieve a factory based on directive name', inject(function ($$directiveIntrospector) {
function myDir(){}
$compileProvider.directive('myDir', ['foo', myDir]);
expect($$directiveIntrospector.getTypeByName('myDir')).toBe(myDir);
}));
});

View File

@ -1,102 +0,0 @@
'use strict';
describe('ngOutlet animations', function () {
var elt,
$animate,
$compile,
$rootScope,
$router,
$compileProvider;
beforeEach(function () {
module('ng');
module('ngAnimate');
module('ngAnimateMock');
module('ngComponentRouter');
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_) {
$animate = _$animate_;
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
});
registerComponent('userCmp', {
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
});
});
afterEach(function () {
expect($animate.queue).toEqual([]);
});
it('should work in a simple case', function () {
var item;
compile('<div ng-outlet></div>');
$router.config([
{ path: '/user/:name', component: 'userCmp' }
]);
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
// "user" component enters
item = $animate.queue.shift();
expect(item.event).toBe('enter');
// navigate to pete
$router.navigateByUrl('/user/pete');
$rootScope.$digest();
expect(elt.text()).toBe('hello pete');
// "user pete" component enters
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('hello pete');
// "user brian" component leaves
item = $animate.queue.shift();
expect(item.event).toBe('leave');
expect(item.element.text()).toBe('hello brian');
});
function registerComponent(name, options) {
var controller = options.controller || function () {};
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
$compileProvider.directive(name, factory);
}
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
});

View File

@ -1,487 +0,0 @@
'use strict';
describe('Navigation lifecycle', function () {
var elt,
$compile,
$rootScope,
$router,
$compileProvider;
beforeEach(function () {
module('ng');
module('ngComponentRouter');
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
});
registerComponent('oneCmp', {
template: '<div>{{oneCmp.number}}</div>',
controller: function () {this.number = 'one'}
});
registerComponent('twoCmp', {
template: '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>',
controller: function () {this.number = 'two'}
});
});
it('should run the activate hook of controllers', function () {
var spy = jasmine.createSpy('activate');
registerComponent('activateCmp', {
template: '<p>hello</p>',
$onActivate: spy
});
$router.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div>outer { <div ng-outlet></div> }</div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
});
it('should pass instruction into the activate hook of a controller', function () {
var spy = jasmine.createSpy('activate');
registerComponent('userCmp', {
$onActivate: spy
});
$router.config([
{ path: '/user/:name', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
});
it('should pass previous instruction into the activate hook of a controller', function () {
var spy = jasmine.createSpy('activate');
var activate = registerComponent('activateCmp', {
template: 'hi',
$onActivate: spy
});
$router.config([
{ path: '/user/:name', component: 'oneCmp' },
{ path: '/post/:id', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
$router.navigateByUrl('/post/123');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
instructionFor('oneCmp'));
});
it('should inject $scope into the controller constructor', function () {
var injectedScope;
registerComponent('userCmp', {
template: '',
controller: function ($scope) {
injectedScope = $scope;
}
});
$router.config([
{ path: '/user', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user');
$rootScope.$digest();
expect(injectedScope).toBeDefined();
});
it('should run the deactivate hook of controllers', function () {
var spy = jasmine.createSpy('deactivate');
registerComponent('deactivateCmp', {
$onDeactivate: spy
});
$router.config([
{ path: '/a', component: 'deactivateCmp' },
{ path: '/b', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
$router.navigateByUrl('/b');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
});
it('should pass instructions into the deactivate hook of controllers', function () {
var spy = jasmine.createSpy('deactivate');
registerComponent('deactivateCmp', {
$onDeactivate: spy
});
$router.config([
{ path: '/user/:name', component: 'deactivateCmp' },
{ path: '/post/:id', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
$router.navigateByUrl('/post/123');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
instructionFor('deactivateCmp'));
});
it('should run the deactivate hook before the activate hook', function () {
var log = [];
registerComponent('activateCmp', {
$onActivate: function () {
log.push('activate');
}
});
registerComponent('deactivateCmp', {
$onDeactivate: function () {
log.push('deactivate');
}
});
$router.config([
{ path: '/a', component: 'deactivateCmp' },
{ path: '/b', component: 'activateCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootScope.$digest();
$router.navigateByUrl('/b');
$rootScope.$digest();
expect(log).toEqual(['deactivate', 'activate']);
});
it('should reuse a component when the canReuse hook returns true', function () {
var log = [];
var cmpInstanceCount = 0;
function ReuseCmp() {
cmpInstanceCount++;
}
registerComponent('reuseCmp', {
template: 'reuse {<ng-outlet></ng-outlet>}',
$routeConfig: [
{path: '/a', component: 'oneCmp'},
{path: '/b', component: 'twoCmp'}
],
controller: ReuseCmp,
$canReuse: function () {
return true;
},
$onReuse: function (next, prev) {
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
}
});
$router.config([
{ path: '/on-reuse/:number/...', component: 'reuseCmp' },
{ path: '/two', component: 'twoCmp', as: 'Two'}
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/on-reuse/1/a');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {one} }');
$router.navigateByUrl('/on-reuse/2/b');
$rootScope.$digest();
expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {two} }');
});
it('should not reuse a component when the canReuse hook returns false', function () {
var log = [];
var cmpInstanceCount = 0;
function NeverReuseCmp() {
cmpInstanceCount++;
}
registerComponent('reuseCmp', {
template: 'reuse {<ng-outlet></ng-outlet>}',
$routeConfig: [
{path: '/a', component: 'oneCmp'},
{path: '/b', component: 'twoCmp'}
],
controller: NeverReuseCmp,
$canReuse: function () {
return false;
},
$onReuse: function (next, prev) {
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
}
});
$router.config([
{ path: '/never-reuse/:number/...', component: 'reuseCmp' },
{ path: '/two', component: 'twoCmp', as: 'Two'}
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/never-reuse/1/a');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {one} }');
$router.navigateByUrl('/never-reuse/2/b');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(2);
expect(elt.text()).toBe('outer { reuse {two} }');
});
// TODO: need to solve getting ahold of canActivate hook
it('should not activate a component when canActivate returns false', function () {
var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(false);
var spy = jasmine.createSpy('activate');
registerComponent('activateCmp', {
$canActivate: canActivateSpy,
$onActivate: spy
});
$router.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).not.toHaveBeenCalled();
expect(elt.text()).toBe('outer { }');
});
it('should activate a component when canActivate returns true', function () {
var activateSpy = jasmine.createSpy('activate');
var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(true);
registerComponent('activateCmp', {
template: 'hi',
$canActivate: canActivateSpy,
$onActivate: activateSpy
});
$router.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(canActivateSpy).toHaveBeenCalled();
expect(activateSpy).toHaveBeenCalled();
expect(elt.text()).toBe('hi');
});
it('should activate a component when canActivate returns a resolved promise', inject(function ($q) {
var spy = jasmine.createSpy('activate');
registerComponent('activateCmp', {
template: 'hi',
$canActivate: function () {
return $q.when(true);
},
$onActivate: spy
});
$router.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
expect(elt.text()).toBe('hi');
}));
it('should inject into the canActivate hook of controllers', inject(function ($http) {
var spy = jasmine.createSpy('canActivate').and.returnValue(true);
registerComponent('activateCmp', {
$canActivate: spy
});
spy.$inject = ['$nextInstruction', '$http'];
$router.config([
{ path: '/user/:name', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
var args = spy.calls.mostRecent().args;
expect(args[0].params).toEqual({name: 'brian'});
expect(args[1]).toBe($http);
}));
it('should not navigate when canDeactivate returns false', function () {
registerComponent('activateCmp', {
template: 'hi',
$canDeactivate: function () {
return false;
}
});
$router.config([
{ path: '/a', component: 'activateCmp' },
{ path: '/b', component: 'oneCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.text()).toBe('outer { hi }');
$router.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { hi }');
});
it('should navigate when canDeactivate returns true', function () {
registerComponent('activateCmp', {
template: 'hi',
$canDeactivate: function () {
return true;
}
});
$router.config([
{ path: '/a', component: 'activateCmp' },
{ path: '/b', component: 'oneCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.text()).toBe('outer { hi }');
$router.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { one }');
});
it('should activate a component when canActivate returns true', function () {
var spy = jasmine.createSpy('activate');
registerComponent('activateCmp', {
template: 'hi',
$canActivate: function () {
return true;
},
$onActivate: spy
});
$router.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
expect(elt.text()).toBe('hi');
});
it('should pass instructions into the canDeactivate hook of controllers', function () {
var spy = jasmine.createSpy('canDeactivate').and.returnValue(true);
registerComponent('deactivateCmp', {
$canDeactivate: spy
});
$router.config([
{ path: '/user/:name', component: 'deactivateCmp' },
{ path: '/post/:id', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
$router.navigateByUrl('/post/123');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
instructionFor('deactivateCmp'));
});
function registerComponent(name, options) {
var controller = options.controller || function () {};
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
$compileProvider.directive(name, factory);
}
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
function instructionFor(componentType) {
return jasmine.objectContaining({componentType: componentType});
}
});

View File

@ -1,264 +0,0 @@
'use strict';
describe('navigation', function () {
var elt,
$compile,
$rootScope,
$router,
$compileProvider;
beforeEach(function () {
module('ng');
module('ngComponentRouter');
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
});
registerComponent('userCmp', {
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
});
registerComponent('oneCmp', {
template: '<div>{{oneCmp.number}}</div>',
controller: function () {this.number = 'one'}
});
registerComponent('twoCmp', {
template: '<div>{{twoCmp.number}}</div>',
controller: function () {this.number = 'two'}
});
});
it('should work in a simple case', function () {
compile('<ng-outlet></ng-outlet>');
$router.config([
{ path: '/', component: 'oneCmp' }
]);
$router.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('one');
});
it('should navigate between components with different parameters', function () {
$router.config([
{ path: '/user/:name', component: 'userCmp' }
]);
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
$router.navigateByUrl('/user/igor');
$rootScope.$digest();
expect(elt.text()).toBe('hello igor');
});
it('should reuse a parent when navigating between child components with different parameters', function () {
var instanceCount = 0;
function ParentController() {
instanceCount += 1;
}
registerComponent('parentCmp', {
template: 'parent { <ng-outlet></ng-outlet> }',
$routeConfig: [
{ path: '/user/:name', component: 'userCmp' }
],
controller: ParentController
});
$router.config([
{ path: '/parent/...', component: 'parentCmp' }
]);
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/parent/user/brian');
$rootScope.$digest();
expect(instanceCount).toBe(1);
expect(elt.text()).toBe('parent { hello brian }');
$router.navigateByUrl('/parent/user/igor');
$rootScope.$digest();
expect(instanceCount).toBe(1);
expect(elt.text()).toBe('parent { hello igor }');
});
it('should work with nested outlets', function () {
registerComponent('childCmp', {
template: '<div>inner { <div ng-outlet></div> }</div>',
$routeConfig: [
{ path: '/b', component: 'oneCmp' }
]
});
$router.config([
{ path: '/a/...', component: 'childCmp' }
]);
compile('<div>outer { <div ng-outlet></div> }</div>');
$router.navigateByUrl('/a/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { inner { one } }');
});
// TODO: fix this
xit('should work with recursive nested outlets', function () {
registerComponent('recurCmp', {
template: '<div>recur { <div ng-outlet></div> }</div>',
$routeConfig: [
{ path: '/recur', component: 'recurCmp' },
{ path: '/end', component: 'oneCmp' }
]});
$router.config([
{ path: '/recur', component: 'recurCmp' },
{ path: '/', component: 'oneCmp' }
]);
compile('<div>root { <div ng-outlet></div> }</div>');
$router.navigateByUrl('/recur/recur/end');
$rootScope.$digest();
expect(elt.text()).toBe('root { one }');
});
it('should change location path', inject(function ($location) {
$router.config([
{ path: '/user', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user');
$rootScope.$digest();
expect($location.path()).toBe('/user');
}));
it('should change location to the canonical route', inject(function ($location) {
compile('<div ng-outlet></div>');
$router.config([
{ path: '/', redirectTo: '/user' },
{ path: '/user', component: 'userCmp' }
]);
$router.navigateByUrl('/');
$rootScope.$digest();
expect($location.path()).toBe('/user');
}));
it('should change location to the canonical route with nested components', inject(function ($location) {
registerComponent('childRouter', {
template: '<div>inner { <div ng-outlet></div> }</div>',
$routeConfig: [
{ path: '/old-child', redirectTo: '/new-child' },
{ path: '/new-child', component: 'oneCmp'},
{ path: '/old-child-two', redirectTo: '/new-child-two' },
{ path: '/new-child-two', component: 'twoCmp'}
]
});
$router.config([
{ path: '/old-parent', redirectTo: '/new-parent' },
{ path: '/new-parent/...', component: 'childRouter' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/old-parent/old-child');
$rootScope.$digest();
expect($location.path()).toBe('/new-parent/new-child');
expect(elt.text()).toBe('inner { one }');
$router.navigateByUrl('/old-parent/old-child-two');
$rootScope.$digest();
expect($location.path()).toBe('/new-parent/new-child-two');
expect(elt.text()).toBe('inner { two }');
}));
it('should navigate when the location path changes', inject(function ($location) {
$router.config([
{ path: '/one', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$location.path('/one');
$rootScope.$digest();
expect(elt.text()).toBe('one');
}));
it('should expose a "navigating" property on $router', inject(function ($q) {
var defer;
registerComponent('pendingActivate', {
$canActivate: function () {
defer = $q.defer();
return defer.promise;
}
});
$router.config([
{ path: '/pending-activate', component: 'pendingActivate' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/pending-activate');
$rootScope.$digest();
expect($router.navigating).toBe(true);
defer.resolve();
$rootScope.$digest();
expect($router.navigating).toBe(false);
}));
function registerComponent(name, options) {
var controller = options.controller || function () {};
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
$compileProvider.directive(name, factory);
}
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
});

View File

@ -1,182 +0,0 @@
'use strict';
describe('ngRoute shim', function () {
var elt,
$compile,
$rootScope,
$router,
$compileProvider,
$routeProvider;
beforeEach(function () {
module('ng');
module('ngComponentRouter');
module('ngRouteShim');
module(function (_$compileProvider_, _$routeProvider_) {
$compileProvider = _$compileProvider_;
$routeProvider = _$routeProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
});
});
it('should work in a simple case', function () {
$routeProvider.when('/', {
controller: function OneController() {
this.number = 'one';
},
controllerAs: 'oneCmp',
template: '{{oneCmp.number}}'
});
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('one');
});
it('should adapt routes with templateUrl', inject(function ($templateCache) {
$routeProvider.when('/', {
controller: function OneController() {
this.number = 'one';
},
controllerAs: 'oneCmp',
templateUrl: '/foo'
});
$templateCache.put('/foo', [200, '{{oneCmp.number}}', {}]);
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('root {one}');
}));
it('should adapt routes using the "resolve" option', inject(function ($q) {
$routeProvider.when('/', {
controller: function TestController(resolvedService) {
this.resolvedValue = resolvedService;
},
controllerAs: 'testCmp',
resolve: {
resolvedService: function () {
return $q.when(42);
}
},
template: 'value: {{testCmp.resolvedValue}}'
});
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('value: 42');
}));
it('should adapt routes with params', function () {
$routeProvider.when('/user/:name', {
controller: function UserController($routeParams) {
this.$routeParams = $routeParams;
},
controllerAs: 'userCmp',
template: 'hello {{userCmp.$routeParams.name}}'
});
$rootScope.$digest();
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
$router.navigateByUrl('/user/igor');
$rootScope.$digest();
expect(elt.text()).toBe('hello igor');
});
it('should adapt routes with wildcard params', function () {
$routeProvider.when('/home/:params*', {
controller: function UserController($routeParams) {
this.$routeParams = $routeParams;
},
controllerAs: 'homeCmp',
template: 'rest: {{homeCmp.$routeParams.params}}'
});
$rootScope.$digest();
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/home/foo/bar/123');
$rootScope.$digest();
expect(elt.text()).toBe('rest: foo/bar/123');
});
it('should warn about and ignore routes with optional params', function () {
spyOn(console, 'warn');
$routeProvider.when('/home/:params?', {
template: 'home'
});
$rootScope.$digest();
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/home/test');
$rootScope.$digest();
expect(elt.text()).toBe('root {}');
expect(console.warn)
.toHaveBeenCalledWith('Route for "/home/:params?" ignored because it has optional parameters. Skipping.',
'(1 skipped / 0 success / 1 total)');
});
it('should adapt routes with redirects', inject(function ($location) {
$routeProvider
.when('/', {
redirectTo: '/home'
})
.when('/home', {
template: 'welcome home!'
});
$rootScope.$digest();
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('root {welcome home!}');
expect($location.path()).toBe('/home');
}));
//TODO: this is broken in recognition. un-xit this when https://github.com/angular/angular/issues/4133 is fixed
xit('should adapt "otherwise" routes', inject(function ($location) {
$routeProvider
.when('/home', {
template: 'welcome home!'
})
.otherwise({
redirectTo: '/home'
});
$rootScope.$digest();
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/somewhere');
$rootScope.$digest();
expect(elt.text()).toBe('root {welcome home!}');
expect($location.path()).toBe('/home');
}));
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
});

View File

@ -1,170 +0,0 @@
'use strict';
describe('ngLink', function () {
var elt,
$compile,
$rootScope,
$router,
$compileProvider;
beforeEach(function () {
module('ng');
module('ngComponentRouter');
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
});
registerComponent('userCmp', '<div>hello {{userCmp.$routeParams.name}}</div>', function () {});
registerComponent('oneCmp', '<div>{{oneCmp.number}}</div>', function () {this.number = 'one'});
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>', function () {this.number = 'two'});
});
it('should allow linking from the parent to the child', function () {
$router.config([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', as: 'Two' }
]);
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b');
});
it('should allow linking from the child and the parent', function () {
$router.config([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', as: 'Two' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b');
});
it('should allow params in routerLink directive', function () {
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
$router.config([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b/lol');
});
it('should update the href of links with bound params', function () {
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: twoLinkCmp.number}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'param'});
$router.config([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b/param');
});
it('should navigate on left-mouse click when a link url matches a route', function () {
$router.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', as: 'Two'}
]);
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
$rootScope.$digest();
expect(elt.text()).toBe('link | one');
expect(elt.find('a').attr('href')).toBe('./two');
elt.find('a')[0].click();
$rootScope.$digest();
expect(elt.text()).toBe('link | two');
});
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($router) {
$router.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', as: 'Two'}
]);
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
$rootScope.$digest();
expect(elt.text()).toBe('link | one');
elt.find('a').triggerHandler({ type: 'click', which: 3 });
$rootScope.$digest();
expect(elt.text()).toBe('link | one');
}));
// See https://github.com/angular/router/issues/206
it('should not navigate a link without an href', function () {
$router.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', as: 'Two'}
]);
expect(function () {
compile('<a>link</a>');
$rootScope.$digest();
expect(elt.text()).toBe('link');
elt.find('a')[0].click();
$rootScope.$digest();
}).not.toThrow();
});
function registerComponent(name, template, config) {
var controller = function () {};
function factory() {
return {
template: template,
controllerAs: name,
controller: controller
};
}
if (!template) {
template = '';
}
if (angular.isArray(config)) {
factory.annotations = [new angular.annotations.RouteConfig(config)];
} else if (typeof config === 'function') {
controller = config;
} else if (typeof config === 'object') {
if (config.canActivate) {
controller.$canActivate = config.canActivate;
}
}
$compileProvider.directive(name, factory);
}
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
});

View File

@ -1,80 +0,0 @@
/*
* Helpers to keep tests DRY
*/
function componentTemplatePath(name) {
return './components/' + dashCase(name) + '/' + dashCase(name) + '.html';
}
function componentControllerName(name) {
return name[0].toUpperCase() + name.substr(1) + 'Controller';
}
function dashCase(str) {
return str.replace(/([A-Z])/g, function ($1) {
return '-' + $1.toLowerCase();
});
}
function provideHelpers(fn, preInject) {
return function () {
var elt,
$compile,
$rootScope,
$router,
$templateCache,
$controllerProvider;
module('ng');
module('ngNewRouter');
module(function(_$controllerProvider_) {
$controllerProvider = _$controllerProvider_;
});
inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$templateCache = _$templateCache_;
});
function registerComponent(name, template, config) {
if (!template) {
template = '';
}
var ctrl;
if (!config) {
ctrl = function () {};
} else if (angular.isArray(config)) {
ctrl = function () {};
ctrl.$routeConfig = config;
} else if (typeof config === 'function') {
ctrl = config;
} else {
ctrl = function () {};
ctrl.prototype = config;
}
$controllerProvider.register(componentControllerName(name), ctrl);
put(name, template);
}
function put (name, template) {
$templateCache.put(componentTemplatePath(name), [200, template, {}]);
}
function compile(template) {
var elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
fn({
registerComponent: registerComponent,
$router: $router,
put: put,
compile: compile
})
}
}

View File

@ -1,12 +0,0 @@
{
"version": "v4",
"repo": "angular/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"angularjs/angular.d.ts": {
"commit": "746b9a892629060bc853e792afff536e0ec4655e"
}
}
}

View File

@ -5,12 +5,12 @@ The sources for this package are in the main [Angular2](https://github.com/angul
This package contains different sources for different users:
1. The files located in the root folder can be consumed using CommonJS.
1. The files located in the root folder can be consumed using CommonJS
2. The files under `/es6` are es6 compatible files that can be transpiled to
es5 using any transpiler. This contains:
* `dev/`: a development version that includes runtime type assertions
* `prod/`: a production version that does not include runtime type assertions
3. The files under `/ts` are the TypeScript source files.
3. The files under `/atscript` are the AtScript source files
As a convenience, we provide you with `/es6/{dev|prod}/es5build.js`, a script to transpile the es6 sources into es5
using [Google Traceur](https://github.com/google/traceur-compiler/).

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