Compare commits
15 Commits
2.0.0-alph
...
starting
Author | SHA1 | Date | |
---|---|---|---|
2800683e64 | |||
a1158a06bd | |||
35a376acdb | |||
b8f0209ba5 | |||
9386bb1cb1 | |||
01592ff773 | |||
2a5d0dd59a | |||
e07a2d10c7 | |||
7234d368e3 | |||
cdc83730df | |||
0f30ce6e92 | |||
fb6b8b22a1 | |||
853c24f4d4 | |||
e37f58a228 | |||
e569e0b1ee |
@ -1,3 +0,0 @@
|
||||
Language: JavaScript
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,18 +1,11 @@
|
||||
.DS_STORE
|
||||
|
||||
# Don’t 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,12 +0,0 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
".git" : true,
|
||||
".idea": true,
|
||||
"node_modules" : true,
|
||||
"bower_components" : true,
|
||||
"packages" : true,
|
||||
"build" : true,
|
||||
"dist" : true,
|
||||
"tmp" : true
|
||||
}
|
||||
}
|
104
.travis.yml
104
.travis.yml
@ -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"
|
||||
|
1310
CHANGELOG.md
1310
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
31
COMMITTER.md
31
COMMITTER.md
@ -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)
|
@ -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].
|
||||
|
||||
|
234
DEVELOPER.md
234
DEVELOPER.md
@ -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.
|
||||
|
||||
|
32
NAMING.md
32
NAMING.md
@ -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
|
||||
|
||||
|
||||
|
43
README.md
43
README.md
@ -1,26 +1,15 @@
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://badge.fury.io/js/angular2)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
|
||||
Angular
|
||||
Angular [](https://travis-ci.org/angular/angular) [](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
|
||||
|
4
TOOLS.md
4
TOOLS.md
@ -1,4 +0,0 @@
|
||||
# Developer Tools for Angular 2
|
||||
|
||||
- [JavaScript](TOOLS_JS.md)
|
||||
- [Dart](TOOLS_DART.md)
|
324
TOOLS_DART.md
324
TOOLS_DART.md
@ -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 -->
|
140
TOOLS_JS.md
140
TOOLS_JS.md
@ -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).
|
@ -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 → *tbosch*, *crossj*
|
||||
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks → *iminar*, *caitp*
|
||||
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart → *mprobst*, *alexeagle*
|
||||
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) →
|
||||
*mhevery*
|
||||
* `comp: core/animations` -- animations framework → *matsko*
|
||||
* `comp: core/change_detection` -- change detection → *vsavkin*
|
||||
* `comp: core/di` -- dependency injection → *vicb*, *rkirov*
|
||||
* `comp: core/directives` -- directives
|
||||
* `comp: core/forms` -- forms → *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 → *kegluneq*, *jakemac*
|
||||
* `comp: data-access` -- → *jeffbcross*
|
||||
* `comp: docs` -- API docs and doc generation → *naomiblack*, *petebacondarwin*
|
||||
* `comp: material-components` -- Angular Material components built in Angular 2 → *jelbourn*
|
||||
* `comp: router` -- Component Router → *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.
|
@ -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
366
docs/app/css/app.css
Normal 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;
|
||||
}
|
142
docs/app/css/prettify-theme.css
Normal file
142
docs/app/css/prettify-theme.css
Normal 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
55
docs/app/index.html
Normal 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
47
docs/app/js/app.js
Normal 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
15
docs/app/js/code.js
Normal 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
20
docs/bower.json
Normal 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
147
docs/dgeni-package/index.js
Normal 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'
|
||||
});
|
||||
});
|
@ -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); });
|
||||
|
||||
};
|
24
docs/dgeni-package/processors/extractTitleFromGuides.js
Normal file
24
docs/dgeni-package/processors/extractTitleFromGuides.js
Normal 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];
|
||||
}
|
||||
}
|
34
docs/dgeni-package/processors/generateDocsFromComments.js
Normal file
34
docs/dgeni-package/processors/generateDocsFromComments.js
Normal 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);
|
||||
}
|
||||
};
|
||||
};
|
66
docs/dgeni-package/processors/generateNavigationDoc.js
Normal file
66
docs/dgeni-package/processors/generateNavigationDoc.js
Normal 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);
|
||||
}
|
||||
};
|
||||
};
|
47
docs/dgeni-package/processors/processClassDocs.js
Normal file
47
docs/dgeni-package/processors/processClassDocs.js
Normal 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);
|
||||
}
|
||||
};
|
||||
};
|
46
docs/dgeni-package/processors/processModuleDocs.js
Normal file
46
docs/dgeni-package/processors/processModuleDocs.js
Normal 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);
|
||||
}
|
||||
};
|
||||
};
|
32
docs/dgeni-package/readers/atScript.js
Normal file
32
docs/dgeni-package/readers/atScript.js
Normal 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;
|
||||
|
||||
|
||||
};
|
55
docs/dgeni-package/readers/atScript.spec.js
Normal file
55
docs/dgeni-package/readers/atScript.spec.js
Normal 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');
|
||||
});
|
||||
|
||||
});
|
32
docs/dgeni-package/readers/ngdoc.js
Normal file
32
docs/dgeni-package/readers/ngdoc.js
Normal 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;
|
||||
};
|
45
docs/dgeni-package/readers/ngdoc.spec.js
Normal file
45
docs/dgeni-package/readers/ngdoc.spec.js
Normal 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
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
47
docs/dgeni-package/services/AttachCommentTreeVisitor.js
Normal file
47
docs/dgeni-package/services/AttachCommentTreeVisitor.js
Normal 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;
|
||||
};
|
103
docs/dgeni-package/services/ExportTreeVisitor.js
Normal file
103
docs/dgeni-package/services/ExportTreeVisitor.js
Normal 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;
|
||||
};
|
6
docs/dgeni-package/services/ParseTreeVisitor.js
Normal file
6
docs/dgeni-package/services/ParseTreeVisitor.js
Normal 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;
|
||||
};
|
5
docs/dgeni-package/services/SourceFile.js
Normal file
5
docs/dgeni-package/services/SourceFile.js
Normal 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;
|
||||
};
|
3
docs/dgeni-package/services/TraceurParser.js
Normal file
3
docs/dgeni-package/services/TraceurParser.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function TraceurParser() {
|
||||
return System.get('transpiler/src/parser').Parser;
|
||||
};
|
74
docs/dgeni-package/services/atParser.js
Normal file
74
docs/dgeni-package/services/atParser.js
Normal 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);
|
||||
}
|
||||
};
|
80
docs/dgeni-package/services/atParser.spec.js
Normal file
80
docs/dgeni-package/services/atParser.spec.js
Normal 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' +
|
||||
'*/'
|
||||
);
|
||||
});
|
||||
});
|
28
docs/dgeni-package/services/getJSDocComment.js
Normal file
28
docs/dgeni-package/services/getJSDocComment.js
Normal 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;
|
||||
};
|
||||
};
|
67
docs/dgeni-package/services/getJSDocComment.spec.js
Normal file
67
docs/dgeni-package/services/getJSDocComment.spec.js
Normal 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'
|
||||
);
|
||||
});
|
||||
});
|
3
docs/dgeni-package/services/traceurOptions.js
Normal file
3
docs/dgeni-package/services/traceurOptions.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function traceurOptions() {
|
||||
return System.get(System.map.traceur + '/src/Options.js').options;
|
||||
};
|
14
docs/dgeni-package/templates/class.template.html
Normal file
14
docs/dgeni-package/templates/class.template.html
Normal 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 %}
|
9
docs/dgeni-package/templates/common.template.html
Normal file
9
docs/dgeni-package/templates/common.template.html
Normal 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 %}
|
3
docs/dgeni-package/templates/data-module.template.js
Normal file
3
docs/dgeni-package/templates/data-module.template.js
Normal file
@ -0,0 +1,3 @@
|
||||
angular.module('{$ doc.moduleName $}', [])
|
||||
|
||||
.value('{$ doc.serviceName $}', {$ doc.value | json $});
|
5
docs/dgeni-package/templates/guide.template.html
Normal file
5
docs/dgeni-package/templates/guide.template.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
{$ doc.description | marked $}
|
||||
{% endblock %}
|
1
docs/dgeni-package/templates/layout/base.template.html
Normal file
1
docs/dgeni-package/templates/layout/base.template.html
Normal file
@ -0,0 +1 @@
|
||||
{% block body %}{% endblock %}
|
16
docs/dgeni-package/templates/module.template.html
Normal file
16
docs/dgeni-package/templates/module.template.html
Normal 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 %}
|
21
docs/public-docs-package/index.js
Normal file
21
docs/public-docs-package/index.js
Normal 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';
|
||||
});
|
51
docs/public-docs-package/processors/filterPublicDocs.js
Normal file
51
docs/public-docs-package/processors/filterPublicDocs.js
Normal 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);
|
||||
|
||||
}
|
||||
};
|
@ -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'
|
||||
];
|
||||
});
|
@ -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 {}; });
|
||||
|
||||
};
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -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 %}
|
@ -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 %}
|
@ -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'
|
||||
});
|
||||
|
||||
});
|
@ -1,4 +0,0 @@
|
||||
export var __esModule = true;
|
||||
export class OKToExport {}
|
||||
export function _thisIsPrivate() {}
|
||||
export var thisIsOK = '!';
|
@ -1,5 +0,0 @@
|
||||
export interface MyInterface {
|
||||
optionalProperty? : string
|
||||
<T, U extends Findable<T>>(param: T) : U
|
||||
new (param: number) : MyInterface
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export class Test {
|
||||
firstItem;
|
||||
constructor() { this.doStuff(); }
|
||||
otherMethod() {}
|
||||
doStuff() {}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export var x = 10;
|
@ -1 +0,0 @@
|
||||
export { x as y} from './privateModule';
|
@ -1 +0,0 @@
|
||||
export var x = 100;
|
@ -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;
|
@ -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;
|
||||
}
|
@ -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; });
|
||||
}
|
@ -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'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
@ -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'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -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 });
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
@ -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;
|
||||
};
|
||||
};
|
@ -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';
|
||||
}
|
||||
};
|
@ -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;
|
||||
};
|
||||
};
|
@ -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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
@ -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']);
|
||||
});
|
||||
});
|
1653
gulpfile.js
1653
gulpfile.js
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
@ -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'));
|
||||
};
|
||||
|
@ -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
1
karma.conf.js
Symbolic link
@ -0,0 +1 @@
|
||||
karma-js.conf.js
|
67
modules/angular1_router/build.js
vendored
67
modules/angular1_router/build.js
vendored
@ -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());
|
||||
};
|
@ -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>
|
@ -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);
|
||||
};
|
@ -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);
|
||||
};
|
66
modules/angular1_router/src/module_template.js
vendored
66
modules/angular1_router/src/module_template.js
vendored
@ -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;
|
||||
}
|
@ -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);
|
349
modules/angular1_router/src/ng_route_shim.js
vendored
349
modules/angular1_router/src/ng_route_shim.js
vendored
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}());
|
@ -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);
|
||||
}));
|
||||
});
|
@ -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;
|
||||
}
|
||||
});
|
@ -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});
|
||||
}
|
||||
});
|
@ -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;
|
||||
}
|
||||
});
|
@ -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;
|
||||
}
|
||||
});
|
170
modules/angular1_router/test/ng_link_spec.js
vendored
170
modules/angular1_router/test/ng_link_spec.js
vendored
@ -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;
|
||||
}
|
||||
});
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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
Reference in New Issue
Block a user