Compare commits

..

2 Commits

1623 changed files with 41476 additions and 55764 deletions

View File

@ -1,16 +1,38 @@
**IMPORTANT**: This repository's issues are reserved for feature requests and bug reports. Do not submit support requests here, see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question.
**Note: for support questions, please use one of these channels:** https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.
* **I'm submitting a ... **
[ ] bug report
[ ] feature request
[ ] support request => Please do not submit support request here, see note at the top of this template.
**Steps to reproduce and a minimal demo of the problem**
_Use https://plnkr.co or similar -- try this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5_
_What steps should we try in your demo to see the problem?_
**Current behavior**
* **Do you want to request a *feature* or report a *bug*?**
**Expected/desired behavior**
* **What is the current behavior?**
**Other information**
* **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
* **What is the expected behavior?**
* **What is the motivation / use case for changing the behavior?**
* **Please tell us about your environment:**
- Angular version: 2.0.0-beta.X
- Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
- Language: [all | TypeScript X.X | ES6/7 | ES5 | Dart]
* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc)

8
.gitignore vendored
View File

@ -21,11 +21,6 @@ tmp
*.js.deps
*.js.map
# Files created by the template compiler
**/*.ngfactory.ts
**/*.css.ts
**/*.css.shim.ts
# Or type definitions we mirror from github
# (NB: these lines are removed in publish-build-artifacts.sh)
**/typings/**/*.d.ts
@ -54,6 +49,3 @@ npm-debug.log
# built dart payload tests
/modules_dart/payload/**/build
# rollup-test output
/modules/rollup-test/dist/

View File

@ -3,190 +3,135 @@ sudo: false
node_js:
- '5.4.1'
addons:
# firefox: "38.0"
apt:
sources:
# needed to install g++ that is used by npms's native modules
- ubuntu-toolchain-r-test
packages:
- g++-4.8
branches:
except:
- g3_v2_0
cache:
directories:
- ./node_modules
- ./.chrome/chromium
# - $HOME/.pub-cache
- $HOME/.pub-cache
- $HOME/.chrome/chromium
#before_cache:
# # Undo the pollution of the typescript_next build before the cache is primed for future use
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
before_cache:
# Undo the pollution of the typescript_next build before the cache is primed for future use
- if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
env:
global:
# - KARMA_JS_BROWSERS=ChromeNoSandbox
# - E2E_BROWSERS=ChromeOnTravis
# - LOGS_DIR=/tmp/angular-build/logs
# - ARCH=linux-x64
# Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
# more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
- CXX=g++-4.8
- KARMA_DART_BROWSERS=DartiumWithWebPlatform
# No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
- KARMA_JS_BROWSERS=ChromeNoSandbox
- E2E_BROWSERS=ChromeOnTravis
- LOGS_DIR=/tmp/angular-build/logs
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=angularteam1
- BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
- ARCH=linux-x64
- DART_DEV_VERSION=latest
- DART_STABLE_VERSION=latest
- DART_CHANNEL=stable
- DART_VERSION=$DART_STABLE_VERSION
# Token for tsd to increase github rate limit
# See https://github.com/DefinitelyTyped/tsd#tsdrc
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
# because those are not visible for pull requests, and those should also be reliable.
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
# (password is in Valentine)
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
# GITHUB_TOKEN_ANGULAR
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
matrix:
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
- CI_MODE=js
- CI_MODE=lint
- CI_MODE=e2e
- CI_MODE=saucelabs_required
- CI_MODE=browserstack_required
- MODE=dart
- MODE=dart DART_CHANNEL=dev
- MODE=saucelabs_required
- MODE=browserstack_required
- MODE=saucelabs_optional
- MODE=browserstack_optional
- MODE=dart_ddc
- MODE=js
- MODE=router
- MODE=build_only
- MODE=typescript_next
- MODE=lint
- MODE=payload
#matrix:
# allow_failures:
# - env: "MODE=saucelabs_optional"
# - env: "MODE=browserstack_optional"
matrix:
allow_failures:
- env: "MODE=saucelabs_optional"
- env: "MODE=browserstack_optional"
# Tracked in https://github.com/angular/angular/issues/7050
- env: "MODE=typescript_next"
addons:
firefox: "38.0"
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_install:
- node tools/analytics/build-analytics start ci job
- node tools/analytics/build-analytics start ci before_install
- echo ${TSDRC} > .tsdrc
- export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
- export DISPLAY=:99.0
- export GIT_SHA=$(git rev-parse HEAD)
- ./scripts/ci/init_android.sh
- sh -e /etc/init.d/xvfb start
# Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
- node tools/analytics/build-analytics success ci before_install
install:
- ./scripts/ci-lite/install.sh
- node tools/analytics/build-analytics start ci install
# Install version of npm that we are locked against
- npm install -g npm@3.5.3
# Install version of Chromium that we are locked against
- ./scripts/ci/install_chromium.sh
# Install version of Dart based on the matrix build variables
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
# Print the size of caches to ease debugging
- du -sh ./node_modules || true
# Install npm dependecies
# check-node-modules will exit(1) if we don't need to install
# we need to manually kick off the postinstall script if check-node-modules exit(0)s
- node tools/npm/check-node-modules --purge && npm install || npm run postinstall
- node tools/analytics/build-analytics success ci install
before_script:
- node tools/analytics/build-analytics start ci before_script
- mkdir -p $LOGS_DIR
- ./scripts/ci/presubmit-queue-setup.sh
- node tools/analytics/build-analytics success ci before_script
script:
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
- node tools/analytics/build-analytics start ci script
- ./scripts/ci/build_and_test.sh ${MODE}
- node tools/analytics/build-analytics success ci script
after_script:
- ./scripts/ci-lite/cleanup.sh
- node tools/analytics/build-analytics start ci after_script
- ./scripts/ci/print-logs.sh
- ./scripts/ci/after-script.sh
- ./scripts/publish/publish-build-artifacts.sh
- node tools/analytics/build-analytics success ci after_script
- tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
#branches:
# except:
# - g3_v2_0
#
#cache:
# directories:
# - $HOME/.pub-cache
# - $HOME/.chrome/chromium
#
#before_cache:
# # Undo the pollution of the typescript_next build before the cache is primed for future use
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
#
#env:
# global:
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
# - CXX=g++-4.8
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
# - KARMA_JS_BROWSERS=ChromeNoSandbox
# - E2E_BROWSERS=ChromeOnTravis
# - LOGS_DIR=/tmp/angular-build/logs
# - SAUCE_USERNAME=angular-ci
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
# - BROWSER_STACK_USERNAME=angularteam1
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
# - ARCH=linux-x64
# - DART_DEV_VERSION=latest
# - DART_STABLE_VERSION=latest
# - DART_CHANNEL=stable
# - DART_VERSION=$DART_STABLE_VERSION
# # Token for tsd to increase github rate limit
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
# # because those are not visible for pull requests, and those should also be reliable.
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
# # (password is in Valentine)
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
# # GITHUB_TOKEN_ANGULAR
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
# matrix:
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
# - MODE=dart
# - MODE=dart DART_CHANNEL=dev
# - MODE=saucelabs_required
# - MODE=browserstack_required
# - MODE=saucelabs_optional
# - MODE=browserstack_optional
# - MODE=dart_ddc
# - MODE=js
# - MODE=router
# - MODE=build_only
# - MODE=typescript_next
# - MODE=lint
#
#matrix:
# allow_failures:
# - env: "MODE=saucelabs_optional"
# - env: "MODE=browserstack_optional"
#
#addons:
# firefox: "38.0"
# apt:
# sources:
# - ubuntu-toolchain-r-test
# packages:
# - g++-4.8
#
#before_install:
# - node tools/analytics/build-analytics start ci job
# - node tools/analytics/build-analytics start ci before_install
# - echo ${TSDRC} > .tsdrc
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
# - export DISPLAY=:99.0
# - export GIT_SHA=$(git rev-parse HEAD)
# - ./scripts/ci/init_android.sh
# - sh -e /etc/init.d/xvfb start
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
# - node tools/analytics/build-analytics success ci before_install
#
#install:
# - node tools/analytics/build-analytics start ci install
# # Install version of npm that we are locked against
# - npm install -g npm@3.5.3
# # Install version of Chromium that we are locked against
# - ./scripts/ci/install_chromium.sh
# # Install version of Dart based on the matrix build variables
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
# # Print the size of caches to ease debugging
# - du -sh ./node_modules || true
# # Install npm dependecies
# # check-node-modules will exit(1) if we don't need to install
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
# - node tools/analytics/build-analytics success ci install
#
#before_script:
# - node tools/analytics/build-analytics start ci before_script
# - mkdir -p $LOGS_DIR
# - ./scripts/ci/presubmit-queue-setup.sh
# - node tools/analytics/build-analytics success ci before_script
#
#script:
# - node tools/analytics/build-analytics start ci script
# - ./scripts/ci/build_and_test.sh ${MODE}
# - node tools/analytics/build-analytics success ci script
#
#after_script:
# - node tools/analytics/build-analytics start ci after_script
# - ./scripts/ci/print-logs.sh
# - ./scripts/ci/after-script.sh
# - ./scripts/publish/publish-build-artifacts.sh
# - node tools/analytics/build-analytics success ci after_script
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
#
#notifications:
# webhooks:
# urls:
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# # trigger Buildtime Trend Service to parse Travis CI log
# - https://buildtimetrend.herokuapp.com/travis
# - http://104.197.9.155:8484/hubot/travis/activity
# on_success: always # options: [always|never|change] default: always
# on_failure: always # options: [always|never|change] default: always
# on_start: never # default: never
# slack:
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# trigger Buildtime Trend Service to parse Travis CI log
- https://buildtimetrend.herokuapp.com/travis
- http://104.197.9.155:8484/hubot/travis/activity
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # default: never
slack:
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=

View File

@ -1,464 +1,3 @@
<a name="2.0.0-rc.0"></a>
# 2.0.0-rc.0 (2016-05-02)
This is the first release candidate that contains repackaging of Angular into individual packages one per each feature area.
All of the packages are now distributed under the @angular npm scope. This changes how Angular is installed via npm and how you import the code.
To install Angular for a browser application please use:
```
npm install --save @angular/core @angular/compiler @angular/common @angular/platform-browser @angular/platform-browser-dynamic rxjs@5.0.0-beta.6 zone.js@0.6.12
```
To import various symbols please adjust the paths in the following way:
- `angular2/core` -> `@angular/core`
- `angular2/compiler` -> `@angular/compiler`
- `angular2/common` -> `@angular/common`
- `angular2/platform/browser` -> `@angular/platform-browser` (applications with precompiled templates) + `@angular/platform-browser-dynamic` (applications that compile templates on the fly)
- `angular2/platform/server` -> `@angular/platform-server`
- `angular2/testing` -> `@angular/core/testing` (it/describe/..) + `@angular/compiler/testing` (TestComponentBuilder) + `@angular/platform-browser/testing`
- `angular2/upgrade` -> `@angular/upgrade`
- `angular2/http` -> `@angular/http`
- `angular2/router` -> `@angular/router-deprecated` (snapshot of the component router from beta.17 for backwards compatibility)
- new package: `@angular/router` - component router with several [breaking changes](https://docs.google.com/document/d/1WLSNV3V1AKdwLwRiLuN7JqbPBKQ_S5quRlcT5LPIldw/edit#heading=h.blfh5ya9sf5r)
### Features
* **core:** introduce template context ([cacdead](https://github.com/angular/angular/commit/cacdead)), closes [#8321](https://github.com/angular/angular/issues/8321)
* **core:** support the decorator data that tsickle produces ([b6fd811](https://github.com/angular/angular/commit/b6fd811))
* **di:** support map literals as providers ([46cd868](https://github.com/angular/angular/commit/46cd868))
* **offline compiler:** a replacement for tsc that compiles templates ([78946fe](https://github.com/angular/angular/commit/78946fe))
* **offline compiler:** add metadata emit ([072446a](https://github.com/angular/angular/commit/072446a))
* **router:** add CanDeactivate ([deba804](https://github.com/angular/angular/commit/deba804))
* **router:** add link that support only absolute urls ([fa5bfe4](https://github.com/angular/angular/commit/fa5bfe4))
* **router:** add Router and RouterOutlet to support aux routes ([6e1fed4](https://github.com/angular/angular/commit/6e1fed4))
* **router:** add RouterLink ([de56dd5](https://github.com/angular/angular/commit/de56dd5))
* **router:** add RouterUrlSerializer ([79830f1](https://github.com/angular/angular/commit/79830f1))
* **router:** add RouteTree and UrlTree as aliases to Tree<RouteSegment> and Tree<UrlSegment> ([277b1fc](https://github.com/angular/angular/commit/277b1fc))
* **router:** add support for wildcards ([8836219](https://github.com/angular/angular/commit/8836219))
* **router:** adds an example app using the new router ([602641d](https://github.com/angular/angular/commit/602641d))
* **router:** change location when navigating ([560cc14](https://github.com/angular/angular/commit/560cc14))
* **router:** implement relative navigation ([e5b87e5](https://github.com/angular/angular/commit/e5b87e5))
* **router:** implements support for router-link-active ([ec4ca0e](https://github.com/angular/angular/commit/ec4ca0e))
* **router:** listen to location changes ([62a0809](https://github.com/angular/angular/commit/62a0809)), closes [#8362](https://github.com/angular/angular/issues/8362)
* **router:** set router-link-active when RouterLink is active ([4fe0f1f](https://github.com/angular/angular/commit/4fe0f1f)), closes [#8376](https://github.com/angular/angular/issues/8376)
* **router:** update recognize to handle matrix parameters ([446657b](https://github.com/angular/angular/commit/446657b))
* **router:** update recognize to support aux routes ([d35c109](https://github.com/angular/angular/commit/d35c109))
* **router:** update url parser to handle aux routes ([fad3b64](https://github.com/angular/angular/commit/fad3b64))
* **testing:** Use NgZone in TestComponentBuilder. ([769835e](https://github.com/angular/angular/commit/769835e)), closes [#8301](https://github.com/angular/angular/issues/8301)
* **tests:** add ROUTER_FAKE_PROVIDERS to angular2/alt_router/router_testing_providers ([0f1b370](https://github.com/angular/angular/commit/0f1b370))
### Bug Fixes
* **metadata:** Preserve Provider expressions ([7c0d497](https://github.com/angular/angular/commit/7c0d497))
* **codegen:** event handler has boolean return type ([ca40ef5](https://github.com/angular/angular/commit/ca40ef5))
* **compiler:** fix cross view references and providers with `useValue`. ([f114d6c](https://github.com/angular/angular/commit/f114d6c)), closes [#8366](https://github.com/angular/angular/issues/8366)
* **compiler:** project using the right directive as component. ([0f774df](https://github.com/angular/angular/commit/0f774df)), closes [#8344](https://github.com/angular/angular/issues/8344)
* **compiler:** support css stylesheets in offline compiler ([00d3b60](https://github.com/angular/angular/commit/00d3b60))
* **compiler:** support empty array and map literals. ([11955f9](https://github.com/angular/angular/commit/11955f9)), closes [#8336](https://github.com/angular/angular/issues/8336)
* **compiler_cli:** make sure the generated code gets compiled via tic ([163d80a](https://github.com/angular/angular/commit/163d80a))
* **core:** check components if an event handler inside of an embedded view fires. ([4d691b6](https://github.com/angular/angular/commit/4d691b6)), closes [#8242](https://github.com/angular/angular/issues/8242)
* **core:** return the ChangeDetectorRef of the component also for embedded views. ([351f24e](https://github.com/angular/angular/commit/351f24e))
* **metadata:** expose Providers in metadata ([8bf6ef6](https://github.com/angular/angular/commit/8bf6ef6))
* **perf:** dont use `try/catch` in production mode ([b1a9e44](https://github.com/angular/angular/commit/b1a9e44)), closes [#8338](https://github.com/angular/angular/issues/8338)
* **router:** canDeactivate should not change the url when returns false ([76d6f5f](https://github.com/angular/angular/commit/76d6f5f)), closes [#8360](https://github.com/angular/angular/issues/8360)
* **router:** create a route tree when creating the router service ([ca13f1c](https://github.com/angular/angular/commit/ca13f1c)), closes [#8365](https://github.com/angular/angular/issues/8365)
* **typescript:** strip abstract keyword from properties in .d.ts ([a84c2d7](https://github.com/angular/angular/commit/a84c2d7)), closes [#8339](https://github.com/angular/angular/issues/8339)
### OTHER BREAKING CHANGES
* - ViewRef.changeDetectorRef was removed as using ChangeDetectorRefs
for EmbeddedViewRefs does not make sense. Use ComponentRef.changeDetectorRef
or inject ChangeDetectorRef instead.
* - Before, a `EmbeddedViewRef` used to have methods for
setting variables. Now, a user has to pass in a context
object that represents all variables when an `EmbeddedViewRef`
should be created.
- `ViewContainerRef.createEmbeddedViewRef` now takes
a context object as 2nd argument.
- `EmbeddedViewRef.setLocal` and `getLocal` have been removed.
Use `EmbeddedViewRef.context` to access the context.
- `DebugNode.locals` has been removed. Use the new methods `DebugElement.references`
to get the references that are present on this element,
or `DebugElement.context` to get the context of the `EmbeddedViewRef` or the component to which the element belongs.
<a name="2.0.0-beta.17"></a>
# 2.0.0-beta.17 (2016-04-28)
### Bug Fixes
* **changelog:** fix changelog script. ([c209836](https://github.com/angular/angular/commit/c209836))
* **compiler:** Allow templates to access variables that are declared afterwards. ([1e8864c](https://github.com/angular/angular/commit/1e8864c)), closes [#8261](https://github.com/angular/angular/issues/8261)
* **core:** properly evaluate expressions with conditional and boolean operators ([1ad2a02](https://github.com/angular/angular/commit/1ad2a02)), closes [#8235](https://github.com/angular/angular/issues/8235) [#8244](https://github.com/angular/angular/issues/8244) [#8282](https://github.com/angular/angular/issues/8282)
* **metadata:** Do not attach module names to metadata. ([d964888](https://github.com/angular/angular/commit/d964888)), closes [#8225](https://github.com/angular/angular/issues/8225) [#8082](https://github.com/angular/angular/issues/8082) [#8256](https://github.com/angular/angular/issues/8256)
* **testing:** allow test component builder to override directives from lists ([ff2ae7a](https://github.com/angular/angular/commit/ff2ae7a)), closes [#7397](https://github.com/angular/angular/issues/7397) [#8217](https://github.com/angular/angular/issues/8217)
### Features
* **compiler:** ElementSchema now has explicit DOM schema information ([d327ac4](https://github.com/angular/angular/commit/d327ac4)), closes [#8179](https://github.com/angular/angular/issues/8179)
* **core:** separate refs from vars. ([d2efac1](https://github.com/angular/angular/commit/d2efac1)), closes [#7158](https://github.com/angular/angular/issues/7158) [#8264](https://github.com/angular/angular/issues/8264)
### BREAKING CHANGES
The reference `#...` now always means `ref-`.
**Before:**
- Outside of `ngFor`, a `#...` meant a reference.
- Inside of `ngFor`, it meant a local variable.
This was pattern was confusing.
**After:**
- `<template #abc>` now defines a reference to a TemplateRef, instead of an input variable used inside of the template.
- Inside of structural directives that declare local variables, such as `*ngFor`, usage of `#...` is deprecated. Use `let` instead.
- `<div *ngFor="#item of items">` now becomes `<div *ngFor="let item of items">`
- `var-...` is deprecated.
- use `#` or a `ref-` outside of `*ngFor`
- for `ngFor`, use the syntax: `<template ngFor let-... [ngForOf]="...">`
<a name="2.0.0-beta.16"></a>
# 2.0.0-beta.16 (2016-04-26)
### Bug Fixes
* **angular_1_router:** Removed arrow function from module template ([d094a85](https://github.com/angular/angular/commit/d094a85)), closes [#8076](https://github.com/angular/angular/issues/8076)
* **build:** ignore Dart warnings for external code. ([4140405](https://github.com/angular/angular/commit/4140405))
* **codegen:** add explicit any to class fields ([c8d00dc](https://github.com/angular/angular/commit/c8d00dc)), closes [#8204](https://github.com/angular/angular/issues/8204) [#8205](https://github.com/angular/angular/issues/8205)
* **compiler:** only call pure pipes if their input changed. ([8db6215](https://github.com/angular/angular/commit/8db6215))
* **compiler:** properly implement pure pipes and change pipe syntax ([152a117](https://github.com/angular/angular/commit/152a117))
* **compiler:** support string tokens with `.` inside. ([cc86fee](https://github.com/angular/angular/commit/cc86fee))
* **compiler:** use DI order for change detection order. ([67d05eb](https://github.com/angular/angular/commit/67d05eb)), closes [#8198](https://github.com/angular/angular/issues/8198)
* **core:** various minor compiler fixes ([2f70457](https://github.com/angular/angular/commit/2f70457)), closes [#8162](https://github.com/angular/angular/issues/8162)
* **forms:** ensure select model updates in firefox and ie ([c3daccd](https://github.com/angular/angular/commit/c3daccd)), closes [#6573](https://github.com/angular/angular/issues/6573) [#8148](https://github.com/angular/angular/issues/8148)
* **forms:** improve error message when ngFormModel is missing a form ([12837e1](https://github.com/angular/angular/commit/12837e1)), closes [#8136](https://github.com/angular/angular/issues/8136) [#8143](https://github.com/angular/angular/issues/8143)
* **forms:** number input should report null when blank ([e69cb40](https://github.com/angular/angular/commit/e69cb40)), closes [#6932](https://github.com/angular/angular/issues/6932) [#8141](https://github.com/angular/angular/issues/8141)
* **metadata:** emit metadata rooted at 'angular2' ([9889c21](https://github.com/angular/angular/commit/9889c21)), closes [#8144](https://github.com/angular/angular/issues/8144) [#8147](https://github.com/angular/angular/issues/8147)
* **release:** Fix the package.json zone.js requirement to 0.6.12 ([6103aa0](https://github.com/angular/angular/commit/6103aa0))
* **tests:** remove payload size check ([22c05b0](https://github.com/angular/angular/commit/22c05b0))
* **transformers:** support `query.read` ([386cc5d](https://github.com/angular/angular/commit/386cc5d)), closes [#8172](https://github.com/angular/angular/issues/8172)
* **upgrade:** clean up scope when element is destroyed ([0fc9ec2](https://github.com/angular/angular/commit/0fc9ec2)), closes [#8102](https://github.com/angular/angular/issues/8102)
### Features
* **codegen:** produce `.ngfactory.dart/ts` files instead of `.template.dart/ts` files. ([c06b0a2](https://github.com/angular/angular/commit/c06b0a2))
* **core:** add `Query.read` and remove `DynamicComponentLoader.loadIntoLocation`. ([efbd446](https://github.com/angular/angular/commit/efbd446))
* **core:** introduce ComponentFactory. ([0c600cf](https://github.com/angular/angular/commit/0c600cf))
* **core:** separate reflective injector from Injector interface ([0a7d10b](https://github.com/angular/angular/commit/0a7d10b))
* **core:** support importUri in StaticReflector ([3e11422](https://github.com/angular/angular/commit/3e11422)), closes [#8195](https://github.com/angular/angular/issues/8195)
* **core:** support non reflective bootstrap. ([9092ac7](https://github.com/angular/angular/commit/9092ac7))
* **html_lexer:** support special forms used by i18n { exp, plural, =0 {} } ([7f29766](https://github.com/angular/angular/commit/7f29766))
* **html_parser:** support special forms used by i18n { exp, plural, =0 {} } ([7c9717b](https://github.com/angular/angular/commit/7c9717b))
* **i18n:** add custom placeholder names ([bb9fb21](https://github.com/angular/angular/commit/bb9fb21)), closes [#7799](https://github.com/angular/angular/issues/7799) [#8057](https://github.com/angular/angular/issues/8057)
* **i18n:** add support for nested expansion forms ([c6244d1](https://github.com/angular/angular/commit/c6244d1)), closes [#7977](https://github.com/angular/angular/issues/7977)
* **i18n:** support plural and gender special forms ([88b0a23](https://github.com/angular/angular/commit/88b0a23))
* **Location:** out of router and into platform/common ([b602bd8](https://github.com/angular/angular/commit/b602bd8)), closes [#7962](https://github.com/angular/angular/issues/7962)
* **NgTemplateOutlet:** add NgTemplateOutlet directive ([f4e6994](https://github.com/angular/angular/commit/f4e6994)), closes [#7615](https://github.com/angular/angular/issues/7615) [#8021](https://github.com/angular/angular/issues/8021)
* **router:** add Router and RouterOutlet ([5a897cf](https://github.com/angular/angular/commit/5a897cf)), closes [#8173](https://github.com/angular/angular/issues/8173)
* **router:** add router metadata ([ef67a0c](https://github.com/angular/angular/commit/ef67a0c))
* **router:** add UrlSegment, RouteSegment, and Tree ([90a1f7d](https://github.com/angular/angular/commit/90a1f7d))
* **router:** implement recognizer ([ef6163e](https://github.com/angular/angular/commit/ef6163e))
* **router:** implement RouterUrlParser ([f698567](https://github.com/angular/angular/commit/f698567))
* **test:** Implement fakeAsync using the FakeAsyncTestZoneSpec from zone.js. ([bab81a9](https://github.com/angular/angular/commit/bab81a9)), closes [#8142](https://github.com/angular/angular/issues/8142)
* **tests:** manage asynchronous tests using zones ([8490921](https://github.com/angular/angular/commit/8490921)), closes [#7735](https://github.com/angular/angular/issues/7735)
* **view_compiler:** codegen DI and Queries ([2b34c88](https://github.com/angular/angular/commit/2b34c88)), closes [#6301](https://github.com/angular/angular/issues/6301) [#6567](https://github.com/angular/angular/issues/6567)
### BREAKING CHANGES
* - pipes now take a variable number of arguments, and not an array that contains all arguments.
* inject can no longer wrap fakeAsync while fakeAsync can wrap inject. So the order in existing tests with inject and fakeAsync has to be switched as follows:
Before:
```
inject([...], fakeAsync((...) => {...}))
```
After:
```
fakeAsync(inject([...], (...) => {...}))
```
You will also need to add the dependency
`'node_modules/zone.js/dist/fake-async-test.js'`
as a served file in your Karma or other test configuration.
* - Injector was renamed into `ReflectiveInjector`,
as `Injector` is only an abstract class with one method on it
- `Injector.getOptional()` was changed into `Injector.get(token, notFoundValue)`
to make implementing injectors simpler
- `ViewContainerRef.createComponent` now takes an `Injector`
instead of `ResolvedProviders`. If a reflective injector
should be used, create one before calling this method.
(e.g. via `ReflectiveInjector.resolveAndCreate(…)`.
* - `DynamicComponentLoader.loadIntoLocation` has been removed. Use `@ViewChild(myVar, read: ViewContainerRef)` to get hold of a `ViewContainerRef` at an element with variable `myVar`. Then call `DynamicComponentLoader.loadNextToLocation`.
- `DynamicComponentLoader.loadNextToLocation` now takes a `ViewContainerRef` instead of an `ElementRef`.
- `AppViewManager` is renamed into `ViewUtils` and is a mere private utility service.
* - `Compiler` is renamed to `ComponentResolver`,
`Compiler.compileInHost` has been renamed to `ComponentResolver.resolveComponent`.
- `ComponentRef.dispose` is renamed to `ComponentRef.destroy`
- `ViewContainerRef.createHostView` is renamed to `ViewContainerRef.createComponent`
- `ComponentFixture_` has been removed, the class `ComponentFixture`
can now be created directly as it is no more using private APIs.
* `Location` and other related providers have been moved out of `router` and into `platform/common`. `BrowserPlatformLocation` is not meant to be used directly however advanced configurations may use it via the following import change.
Before:
```
import {
PlatformLocation,
Location,
LocationStrategy,
HashLocationStrategy,
PathLocationStrategy,
APP_BASE_HREF}
from 'angular2/router';
import {BrowserPlatformLocation} from 'angular2/src/router/location/browser_platform_location';
```
After:
```
import {
PlatformLocation,
Location,
LocationStrategy,
HashLocationStrategy,
PathLocationStrategy,
APP_BASE_HREF}
from 'angular2/platform/common';
import {BrowserPlatformLocation} from 'angular2/src/platform/browser/location/browser_platform_location';
```
* `injectAsync` is now deprecated. Instead, use the `async` function
to wrap any asynchronous tests.
You will also need to add the dependency
`'node_modules/zone.js/dist/async-test.js'`
as a served file in your Karma or other test configuration.
Before:
```
it('should wait for returned promises', injectAsync([FancyService], (service) => {
return service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
}));
it('should wait for returned promises', injectAsync([], () => {
return somePromise.then(() => { expect(true).toEqual(true); });
}));
```
After:
```
it('should wait for returned promises', async(inject([FancyService], (service) => {
service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
})));
// Note that if there is no injection, we no longer need `inject` OR `injectAsync`.
it('should wait for returned promises', async(() => {
somePromise.then() => { expect(true).toEqual(true); });
}));
```
* - Renderer:
* renderComponent method is removed form `Renderer`, only present on `RootRenderer`
* Renderer.setDebugInfo is removed. Renderer.createElement / createText / createTemplateAnchor
now take the DebugInfo directly.
- Query semantics:
* Queries don't work with dynamically loaded components.
* e.g. for router-outlet: loaded components can't be queries via @ViewQuery,
but router-outlet emits an event `activate` now that emits the activated component
- Exception classes and the context inside changed (renamed fields)
- DebugElement.attributes is an Object and not a Map in JS any more
- ChangeDetectorGenConfig was renamed into CompilerConfig
- AppViewManager.createEmbeddedViewInContainer / AppViewManager.createHostViewInContainer
are removed, use the methods in ViewContainerRef instead
- Change detection order changed:
* 1. dirty check component inputs
* 2. dirty check content children
* 3. update render nodes
<a name="2.0.0-beta.15"></a>
# 2.0.0-beta.15 (2016-04-13)
### Bug Fixes
* **7837:** MetadataCollector takes no parameters for the constructor. ([c17dc1c](https://github.com/angular/angular/commit/c17dc1c)), closes [#7838](https://github.com/angular/angular/issues/7838)
* **7987:** Incremental build works with new trees ([08b2956](https://github.com/angular/angular/commit/08b2956)), closes [#7989](https://github.com/angular/angular/issues/7989)
* **build:** ignore dart warnings `The name … is shown, but not used` ([01e6b8c](https://github.com/angular/angular/commit/01e6b8c)), closes [#8045](https://github.com/angular/angular/issues/8045)
* **payload:** increase payload size limit temporarily ([28e657d](https://github.com/angular/angular/commit/28e657d))
* **RouterLink:** ignore optional parameters when checking for active routes ([5e2bc5c](https://github.com/angular/angular/commit/5e2bc5c)), closes [#6459](https://github.com/angular/angular/issues/6459) [#7834](https://github.com/angular/angular/issues/7834)
* **select:** set value individually from ngModel ([e1e44a9](https://github.com/angular/angular/commit/e1e44a9)), closes [#7975](https://github.com/angular/angular/issues/7975) [#7978](https://github.com/angular/angular/issues/7978)
* **upgrade:** make upgradeAdapter upgrade angular 1 components correctly ([247964a](https://github.com/angular/angular/commit/247964a)), closes [#7951](https://github.com/angular/angular/issues/7951)
### Features
* **compiler:** Add an implementation for XHR that uses a template cache to load template files. ([a596b88](https://github.com/angular/angular/commit/a596b88)), closes [#7940](https://github.com/angular/angular/issues/7940)
* **gestures:** allow override of Hammer default configuration ([6cbf990](https://github.com/angular/angular/commit/6cbf990)), closes [#7924](https://github.com/angular/angular/issues/7924)
* **ngFor:** Support convenience view local in ngFor ([ccff175](https://github.com/angular/angular/commit/ccff175)), closes [#8013](https://github.com/angular/angular/issues/8013)
* **parser:** TemplateParser.tryParse() returns both the AST and errors ([226e662](https://github.com/angular/angular/commit/226e662)), closes [#7858](https://github.com/angular/angular/issues/7858)
* **transformers:** changes transformers to collect information about providers and resolve identifi ([3b60503](https://github.com/angular/angular/commit/3b60503))
* **transformers:** special case Profiler ([83b8f59](https://github.com/angular/angular/commit/83b8f59))
* **typescript:** update to 1.9 nightly. ([3412aba](https://github.com/angular/angular/commit/3412aba)), closes [#8003](https://github.com/angular/angular/issues/8003)
### BREAKING CHANGES
* In Dart files, `import 'package:angular2/bootstrap.dart'` no longer works.
Instead, use `import 'package:angular2/platform/browser.dart'`.
### Reverts
* Revert "chore(format): update to latest formatter" ([60727c4](https://github.com/angular/angular/commit/60727c4))
<a name="2.0.0-beta.14"></a>
# 2.0.0-beta.14 (2016-04-07)
### Bug Fixes
* **forms:** support both value and ng-value ([8db97b0](https://github.com/angular/angular/commit/8db97b0))
* **router:** allow forward slashes in query parameters ([4902244](https://github.com/angular/angular/commit/4902244)), closes [#7824](https://github.com/angular/angular/issues/7824)
* **select:** support objects as select values ([74e2bd7](https://github.com/angular/angular/commit/74e2bd7)), closes [#4843](https://github.com/angular/angular/issues/4843) [#7842](https://github.com/angular/angular/issues/7842)
* **select:** update name from ng-value to ngValue ([3ca6df8](https://github.com/angular/angular/commit/3ca6df8)), closes [#7939](https://github.com/angular/angular/issues/7939)
* **upgrade:** leak when angular1 destroys element ([9be04f8](https://github.com/angular/angular/commit/9be04f8)), closes [#6401](https://github.com/angular/angular/issues/6401) [#7935](https://github.com/angular/angular/issues/7935)
### Features
* **dart/transform:** Avoid `print` in transformer code. ([e310bee](https://github.com/angular/angular/commit/e310bee)), closes [#7855](https://github.com/angular/angular/issues/7855)
* **static-reflector:** Added StaticReflector ([0dbf959](https://github.com/angular/angular/commit/0dbf959))
<a name="2.0.0-beta.13"></a>
# 2.0.0-beta.13 (2016-03-31)
### Bug Fixes
* **build:** MetadataCollector correctly collects property metadata ([111afcd](https://github.com/angular/angular/commit/111afcd)), closes [#7772](https://github.com/angular/angular/issues/7772) [#7773](https://github.com/angular/angular/issues/7773)
* **codegen:** stringify using an opaque ID when toString contains parens. ([90c87fa](https://github.com/angular/angular/commit/90c87fa)), closes [#7825](https://github.com/angular/angular/issues/7825)
* **ngFor:** give more instructive error when binding to non-iterable ([49527ab](https://github.com/angular/angular/commit/49527ab))
* **Router:** handling of special chars in dynamic segments ([0bcfcde](https://github.com/angular/angular/commit/0bcfcde)), closes [#7804](https://github.com/angular/angular/issues/7804)
* **upgrade:** make ngUpgrade work with testability API ([430f367](https://github.com/angular/angular/commit/430f367)), closes [#7603](https://github.com/angular/angular/issues/7603)
### Features
* **build:** Persisting decorator metadata ([ae876d1](https://github.com/angular/angular/commit/ae876d1))
* **compiler:** assert that Component.style is an array ([6de68e2](https://github.com/angular/angular/commit/6de68e2)), closes [#7559](https://github.com/angular/angular/issues/7559)
* **compiler:** Resolvers now use DI to create reflector ([506f4ce](https://github.com/angular/angular/commit/506f4ce)), closes [#7762](https://github.com/angular/angular/issues/7762)
* **Compiler:** Allow overriding the projection selector ([aa966f5](https://github.com/angular/angular/commit/aa966f5)), closes [#6303](https://github.com/angular/angular/issues/6303) [#7742](https://github.com/angular/angular/issues/7742)
* **dart:** Add a dev-mode check for undeclared lifecycle interfaces ([1c20a62](https://github.com/angular/angular/commit/1c20a62)), closes [#6849](https://github.com/angular/angular/issues/6849)
* **facade:** add ListWrapper.flatten ([a1880c3](https://github.com/angular/angular/commit/a1880c3))
* **facade:** add RegExpWrapper.replaceAll to replace all matches using the provided function ([91999e0](https://github.com/angular/angular/commit/91999e0))
* **html_parser:** change HtmlElementAst to store both the start and the end positions ([17c8ec8](https://github.com/angular/angular/commit/17c8ec8))
* **i18n:** implement an i18n-aware html parser ([d272f96](https://github.com/angular/angular/commit/d272f96)), closes [#7738](https://github.com/angular/angular/issues/7738)
* **i18n:** implement xmb deserialization ([d7e1175](https://github.com/angular/angular/commit/d7e1175))
* **i18n:** reexport I18nHtmlParser through the i18n barrel ([d2ca7d8](https://github.com/angular/angular/commit/d2ca7d8))
* **i18n:** update I18nHtmlParser to accept parsed messages ([756121a](https://github.com/angular/angular/commit/756121a))
* **i18n:** update transformers to read a xmb file when provided and use I18nHtmlParser in t ([8430927](https://github.com/angular/angular/commit/8430927)), closes [#7790](https://github.com/angular/angular/issues/7790)
### BREAKING CHANGES
* For static content projection, elements with *-directives are now matched against the element itself vs the template before.
`<p *ngIf="condition" foo></p>`
Before:
```html
// Use the implicit template for projection
<ng-content select="template"></ng-content>
```
After:
```html
// Use the actual element for projection
<ng-content select="p[foo]"></ng-content>
```
<a name="2.0.0-beta.12"></a>
# 2.0.0-beta.12 (2016-03-23)
### Bug Fixes
* **angular_1_router:** ng-link is generating wrong hrefs ([69c1405](https://github.com/angular/angular/commit/69c1405)), closes [#7423](https://github.com/angular/angular/issues/7423)
* **angular1_router:** support link generation with custom hashPrefixes ([0f8efce](https://github.com/angular/angular/commit/0f8efce))
* **package.json:** remove es6-promise from the peerDependency list ([8b67b07](https://github.com/angular/angular/commit/8b67b07))
### Features
* **dart/transform:** Use angular2/platform/browser as bootstrap lib ([b6507e3](https://github.com/angular/angular/commit/b6507e3)), closes [#7647](https://github.com/angular/angular/issues/7647)
<a name="2.0.0-beta.11"></a>
# 2.0.0-beta.11 (2016-03-18)
### Bug Fixes
* make sure that Zone does not show up in angular2.d.ts ([d4e9b55](https://github.com/angular/angular/commit/d4e9b55fb69d87f948d02905d34fc78221adb11a))
* **common:** remove @internal annotation on SwitchView ([967ae3e](https://github.com/angular/angular/commit/967ae3e)), closes [#7657](https://github.com/angular/angular/issues/7657)
* **router:** RouterOutlet loads component twice in a race condition ([2f581ff](https://github.com/angular/angular/commit/2f581ff)), closes [#7497](https://github.com/angular/angular/issues/7497) [#7545](https://github.com/angular/angular/issues/7545)
### Features
* **i18n:** add a simple dart script extracting all i18n messages from a package ([8326ab3](https://github.com/angular/angular/commit/8326ab3)), closes [#7620](https://github.com/angular/angular/issues/7620)
* **i18n:** create i18n barrel ([a7fe983](https://github.com/angular/angular/commit/a7fe983))
* **i18n:** implement xmb serializer ([e1f8e54](https://github.com/angular/angular/commit/e1f8e54))
<a name="2.0.0-beta.10"></a>
# 2.0.0-beta.10 (2016-03-17)
### Bug Fixes
* **change_detection:** fix a memory leak ([128acbb](https://github.com/angular/angular/commit/128acbb))
* **closure:** don't throw from top-level ([5824866](https://github.com/angular/angular/commit/5824866))
* **router:** handle URL that does not match a route ([8e3e450](https://github.com/angular/angular/commit/8e3e450)), closes [#7349](https://github.com/angular/angular/issues/7349) [#7203](https://github.com/angular/angular/issues/7203)
* **router/instruction:** ensure toLinkUrl includes extra params ([0d58b13](https://github.com/angular/angular/commit/0d58b13)), closes [#7367](https://github.com/angular/angular/issues/7367)
### Features
* **compiler:** change html parser to preserve comments ([70d18b5](https://github.com/angular/angular/commit/70d18b5))
* **core:** introduce a CSS lexer/parser ([b72bab4](https://github.com/angular/angular/commit/b72bab4))
* **core:** introduce a CSS lexer/parser ([293fa55](https://github.com/angular/angular/commit/293fa55))
* **facade:** add .values to StringMapWrapper ([f1796d6](https://github.com/angular/angular/commit/f1796d6))
* **i18n:** add ngPlural directive ([df1f78e](https://github.com/angular/angular/commit/df1f78e))
* **i18n:** implement a simple version of message extractor ([095db67](https://github.com/angular/angular/commit/095db67)), closes [#7454](https://github.com/angular/angular/issues/7454)
* **shadow_css:** support `/deep/` and `>>>` ([cb38d72](https://github.com/angular/angular/commit/cb38d72)), closes [#7562](https://github.com/angular/angular/issues/7562) [#7563](https://github.com/angular/angular/issues/7563)
* **TAG_DEFINITIONS:** include <meta> and <base> ([2c7c3e3](https://github.com/angular/angular/commit/2c7c3e3)), closes [#7455](https://github.com/angular/angular/issues/7455)
### BREAKING CHANGES
Removed deprecated API from NgZone
- `NgZone.overrideOnTurnStart`
- `NgZone.overrideOnTurnDone`
- `NgZone.overrideOnEventDone`
- `NgZone.overrideOnErrorHandler`
Rename NgZone API
- `NgZone.onTurnStart` => `NgZone.onUnstable`
- `NgZone.onTurnDone` => `NgZone.onMicrotaskEmpty`
- `NgZone.onEventDone` => `NgZone.onStable`
<a name="2.0.0-beta.9"></a>
# 2.0.0-beta.9 (2016-03-09)

View File

@ -1,19 +1,18 @@
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/angular2.svg)](http://badge.fury.io/js/angular2)
[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci)
Angular
Angular
=========
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 **Beta**.
Angular 2 is currently in **Beta**.
## Quickstart

View File

@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
kinds of computation. Example:
```typescript
@Component({
@View({
template: '<button [enabled]="isEnabled">{{title}}</button>'
})
class FancyButton {

View File

@ -4,9 +4,7 @@
var CIconfiguration = {
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
// FirefoxBeta should be required:true
// https://github.com/angular/angular/issues/7560
'FirefoxBeta': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: false}},
'FirefoxBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
@ -38,7 +36,7 @@ var customLaunchers = {
'SL_CHROME': {
base: 'SauceLabs',
browserName: 'chrome',
version: '50'
version: '46'
},
'SL_CHROMEBETA': {
base: 'SauceLabs',
@ -53,7 +51,7 @@ var customLaunchers = {
'SL_FIREFOX': {
base: 'SauceLabs',
browserName: 'firefox',
version: '45'
version: '42'
},
'SL_FIREFOXBETA': {
base: 'SauceLabs',
@ -299,7 +297,12 @@ module.exports = {
customLaunchers: customLaunchers,
sauceAliases: sauceAliases,
browserstackAliases: browserstackAliases
};
}
if (process.env.TRAVIS) {
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
}
function buildConfiguration(type, target, required) {
return Object.keys(CIconfiguration)

112
build.sh
View File

@ -1,112 +0,0 @@
#!/usr/bin/env bash
set -e -o pipefail
cd `dirname $0`
TSCONFIG=./modules/tsconfig.json
echo "====== (all)COMPILING: \$(npm bin)/ng2tc -p ${TSCONFIG} ====="
rm -rf ./dist/all/
mkdir ./dist/all/
# prepare all files for e2e tests
cp -r ./modules/playground ./dist/all/
cp -r ./modules/playground/favicon.ico ./dist/
#rsync -aP ./modules/playground/* ./dist/all/playground/
mkdir ./dist/all/playground/vendor
cd ./dist/all/playground/vendor
ln -s ../../../../node_modules/es6-shim/es6-shim.js .
ln -s ../../../../node_modules/zone.js/dist/zone.js .
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
ln -s ../../../../node_modules/base64-js/lib/b64.js .
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
ln -s ../../../../node_modules/rxjs/bundles/Rx.js .
ln -s ../../../../node_modules/angular/angular.js .
cd -
# compile ts code
$(npm bin)/ng2tc -p ${TSCONFIG}
rm -rf ./dist/packages-dist
for PACKAGE in \
core \
compiler \
common \
platform-browser \
platform-browser-dynamic \
platform-server \
http \
router \
router-deprecated \
upgrade
do
SRCDIR=./modules/@angular/${PACKAGE}
DESTDIR=./dist/packages-dist/${PACKAGE}
UMDES6PATH=${DESTDIR}/esm/${PACKAGE}.umd.js
UMDES5PATH=${DESTDIR}/${PACKAGE}.umd.js
if [[ ${PACKAGE} == "router-deprecated" ]]; then
echo "====== COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json ====="
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json
else
echo "====== COMPILING: \$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es5.json ====="
$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es5.json
fi
cp ${SRCDIR}/package.json ${DESTDIR}/
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
if [[ ${TRAVIS} ]]; then
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
else
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
fi
if [[ ${PACKAGE} == "router-deprecated" ]]; then
echo "====== (esm)COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json ====="
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json
else
echo "====== (esm)COMPILING: \$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es2015.json ====="
$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es2015.json
fi
echo "====== BUNDLING: ${SRCDIR} ====="
(
cd ${SRCDIR}
echo "..." # here just to have grep match something and not exit with 1
../../../node_modules/.bin/rollup -c rollup.config.js
) 2>&1 | grep -v "as external dependency"
# workaround for https://github.com/rollup/rollup/issues/626
if [[ ${TRAVIS} ]]; then
sed -i "s/ class exports\./ class /g" ${DESTDIR}/esm/${PACKAGE}.umd.js
else
sed -i '' "s/ class exports\./ class /g" ${DESTDIR}/esm/${PACKAGE}.umd.js
fi
$(npm bin)/tsc \
--out ${UMDES5PATH} \
--target es5 \
--allowJs \
${UMDES6PATH} \
modules/\@angular/manual_typings/globals.d.ts \
modules/\@angular/typings/es6-collections/es6-collections.d.ts \
modules/\@angular/typings/es6-promise/es6-promise.d.ts
rm ${UMDES6PATH}
cat ./modules/@angular/license-banner.txt > ${UMDES5PATH}.tmp
cat ${UMDES5PATH} >> ${UMDES5PATH}.tmp
mv ${UMDES5PATH}.tmp ${UMDES5PATH}
done

View File

@ -40,9 +40,9 @@ if (cliArgs.projects) {
cliArgs.projects.split(',').sort().join(',');
}
// --projects=angular2 => {angular2: true}
// --projects=angular2,angular2_material => {angular2: true, angular2_material: true}
var allProjects =
'angular1_router,angular2,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
var cliArgsProjects = (cliArgs.projects || allProjects)
.split(',')
.reduce((map, projectName) => {
@ -57,7 +57,7 @@ function printModulesWarning() {
console.warn(
"Pro Tip: Did you know that you can speed up your build by specifying project name(s)?");
console.warn(" It's like pressing the turbo button in the old days, but better!");
console.warn(" Examples: --project=angular2 or --project=angular2");
console.warn(" Examples: --project=angular2 or --project=angular2,angular2_material");
}
}
@ -105,7 +105,7 @@ function runJasmineTests(globs, done) {
var fork = require('child_process').fork;
var args = ['--'].concat(globs);
fork('./dist/tools/cjs-jasmine', args, {stdio: 'inherit'})
fork('./tools/cjs-jasmine', args, {stdio: 'inherit'})
.on('close', function jasmineCloseHandler(exitCode) {
if (exitCode && treatTestErrorsAsFatal) {
var err = new Error('Jasmine tests failed');
@ -132,8 +132,7 @@ var CONFIG = {
dev: {es6: 'dist/js/dev/es6', es5: 'dist/js/dev/es5'},
prod: {es6: 'dist/js/prod/es6', es5: 'dist/js/prod/es5'},
cjs: 'dist/js/cjs',
dart2js: 'dist/js/dart2js',
dart_dev_compiler: 'dist/js/ddc'
dart2js: 'dist/js/dart2js'
},
dart: 'dist/dart',
docs: 'dist/docs',
@ -155,8 +154,6 @@ var NG2_BUNDLE_CONTENT = ANGULAR2_BUNDLE_CONFIG.join(' + ') + ' - rxjs/*';
var HTTP_BUNDLE_CONTENT = 'angular2/http - rxjs/* - ' + ANGULAR2_BUNDLE_CONFIG.join(' - ');
var ROUTER_BUNDLE_CONTENT = 'angular2/router + angular2/router/router_link_dsl - rxjs/* - ' +
ANGULAR2_BUNDLE_CONFIG.join(' - ');
var ALT_ROUTER_BUNDLE_CONTENT =
'angular2/alt_router - rxjs/* - ' + ANGULAR2_BUNDLE_CONFIG.join(' - ');
var TESTING_BUNDLE_CONTENT =
'angular2/testing + angular2/http/testing + angular2/router/testing + angular2/platform/testing/browser - rxjs/* - ' +
ANGULAR2_BUNDLE_CONFIG.join(' - ');
@ -179,8 +176,8 @@ var PAYLOAD_TESTS_CONFIG = {
return path.join(__dirname, CONFIG.dest.js.prod.es5, 'payload_tests', caseName,
'ts/' + packaging);
},
systemjs: {sizeLimits: {'uncompressed': 880 * 1024, 'gzip level=9': 170 * 1024}},
webpack: {sizeLimits: {'uncompressed': 560 * 1024, 'gzip level=9': 130 * 1024}}
systemjs: {sizeLimits: {'uncompressed': 850 * 1024, 'gzip level=9': 165 * 1024}},
webpack: {sizeLimits: {'uncompressed': 550 * 1024, 'gzip level=9': 120 * 1024}}
}
};
@ -300,14 +297,7 @@ function doCheckFormat() {
var clangFormat = require('clang-format');
var gulpFormat = require('gulp-clang-format');
return gulp.src([
'modules/**/*.ts',
'tools/**/*.ts',
'!**/typings/**/*.d.ts',
// workaround https://github.com/angular/clang-format/issues/28
'!tools/compiler_cli/src/main.ts',
'gulpfile.js'
])
return gulp.src(['modules/**/*.ts', 'tools/**/*.ts', '!**/typings/**/*.d.ts', 'gulpfile.js'])
.pipe(gulpFormat.checkFormat('file', clangFormat));
}
@ -379,10 +369,6 @@ function jsServeDartJs() {
return jsserve(gulp, gulpPlugins, {path: CONFIG.dest.js.dart2js, port: 8002})();
}
function jsServeDartDevCompiler() {
return jsserve(gulp, gulpPlugins, {path: CONFIG.dest.js.dart_dev_compiler, port: 8003})();
}
function proxyServeDart() {
return jsserve(gulp, gulpPlugins, {
port: 8002,
@ -396,7 +382,7 @@ function proxyServeDart() {
// ------------------
// web servers
gulp.task('serve.js.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone) {
gulp.task('serve.js.dev', ['build.js.dev'], function(neverDone) {
var watch = require('./tools/build/watch');
watch('modules/**', {ignoreInitial: true}, '!broccoli.js.dev');
@ -405,24 +391,24 @@ gulp.task('serve.js.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone)
gulp.task('serve.js.prod', jsServeProd);
gulp.task('serve.e2e.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone) {
var watch = require('./tools/build/watch');
gulp.task('serve.e2e.dev', ['build.js.dev', 'build.js.cjs', 'build.css.material'],
function(neverDone) {
var watch = require('./tools/build/watch');
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!build.js.cjs']);
jsServeDev();
});
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!build.js.cjs']);
jsServeDev();
});
gulp.task('serve.e2e.prod', ['build.js.prod', 'build.js.cjs'], function(neverDone) {
var watch = require('./tools/build/watch');
gulp.task('serve.e2e.prod', ['build.js.prod', 'build.js.cjs', 'build.css.material'],
function(neverDone) {
var watch = require('./tools/build/watch');
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.prod', '!build.js.cjs']);
jsServeProd();
});
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.prod', '!build.js.cjs']);
jsServeProd();
});
gulp.task('serve.js.dart2js', jsServeDartJs);
gulp.task('serve.js.ddc', jsServeDartDevCompiler);
gulp.task('!proxyServeDart', proxyServeDart);
gulp.task('serve.dart', function(done) {
@ -454,47 +440,29 @@ gulp.task('serve.e2e.dart', ['build.js.cjs'], function(neverDone) {
// Note: we are not using build.dart as the dart analyzer takes too long...
watch('modules/**', {ignoreInitial: true}, ['!build/tree.dart', '!build.js.cjs']);
runSequence('build/packages.dart', 'build/pubspec.dart', 'serve.dart');
runSequence('build/packages.dart', 'build/pubspec.dart', 'build.dart.material.css', 'serve.dart');
});
// ------------------
// CI tests suites
function execProcess(name, args, done) {
function runKarma(configFile, done) {
var exec = require('child_process').exec;
var cmd = process.platform === 'win32' ? 'node_modules\\.bin\\' + name + ' ' :
'node node_modules/.bin/' + name + ' ';
cmd += args;
exec(cmd, done);
}
function runKarma(configFile, done) {
execProcess('karma', 'run ' + configFile, function(e, stdout) {
var cmd = process.platform === 'win32' ? 'node_modules\\.bin\\karma run ' :
'node node_modules/.bin/karma run ';
cmd += configFile;
exec(cmd, function(e, stdout) {
// ignore errors, we don't want to fail the build in the interactive (non-ci) mode
// karma server will print all test failures
done();
});
}
// Gulp-typescript doesn't work with typescript@next:
// https://github.com/ivogabe/gulp-typescript/issues/331
function runTsc(project, done) {
execProcess('tsc', '-p ' + project, function(e, stdout, stderr) {
if (e) {
console.log(stdout);
console.error(stderr);
done(e);
} else {
done();
}
});
}
gulp.task('test.js', function(done) {
runSequence('test.compiler_cli', 'test.unit.tools/ci', 'test.transpiler.unittest',
'test.unit.js/ci', 'test.unit.cjs/ci', 'test.typings', 'check-public-api',
sequenceComplete(done));
runSequence('test.unit.tools/ci', 'test.transpiler.unittest', 'test.unit.js/ci',
'test.unit.cjs/ci', 'test.typings', 'check-public-api', sequenceComplete(done));
});
gulp.task('test.dart', function(done) {
@ -667,7 +635,7 @@ gulp.task('buildRouter.dev', function() {
gulp.task('test.unit.dart', function(done) {
printModulesWarning();
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
'!build/remove-pub-symlinks', function(error) {
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
var watch = require('./tools/build/watch');
// if initial build failed (likely due to build or formatting step) then exit
@ -738,7 +706,7 @@ gulp.task('!build.payload.js.webpack', function() {
.then(function() { // pad bundle with mandatory dependencies
return new Promise(function(resolve, reject) {
gulp.src([
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/reflect-metadata/Reflect.js',
CASE_PATH + '/app-bundle.js'
@ -795,7 +763,7 @@ gulp.task('!checkAndReport.payload.js', function() {
{
failConditions: PAYLOAD_TESTS_CONFIG.ts[packaging].sizeLimits,
prefix: caseName + '_' + packaging
});
})
}
return PAYLOAD_TESTS_CONFIG.ts.cases.reduce(function(sizeReportingStreams, caseName) {
@ -806,7 +774,8 @@ gulp.task('!checkAndReport.payload.js', function() {
gulp.task('watch.dart.dev', function(done) {
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
'!build/remove-pub-symlinks', function(error) {
'!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css',
function(error) {
var watch = require('./tools/build/watch');
// if initial build failed (likely due to build or formatting step) then exit
@ -816,8 +785,7 @@ gulp.task('watch.dart.dev', function(done) {
return;
}
watch(['modules/angular2/**', 'modules_dart/**'], {ignoreInitial: true},
['!build/tree.dart', 'build/pure-packages.dart']);
watch(['modules/angular2/**'], {ignoreInitial: true}, ['!build/tree.dart']);
});
});
@ -923,7 +891,7 @@ gulp.task('test.unit.cjs/ci', function(done) {
runJasmineTests(['dist/js/cjs/{angular2,benchpress}/test/**/*_spec.js'], done);
});
gulp.task('check-public-api', ['build.tools'],
gulp.task('check-public-api',
function(done) { runJasmineTests(['dist/tools/public_api_guard/**/*_spec.js'], done); });
gulp.task('test.unit.cjs', ['build/clean.js', 'build.tools'], function(neverDone) {
@ -939,20 +907,21 @@ gulp.task('test.unit.cjs', ['build/clean.js', 'build.tools'], function(neverDone
gulp.task('test.unit.dartvm', function(neverDone) {
var watch = require('./tools/build/watch');
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
'!test.unit.dartvm/run', function(error) {
// Watch for changes made in the TS and Dart code under "modules" and
// run ts2dart and test change detector generator prior to rerunning the
// tests.
watch('modules/angular2/**', {ignoreInitial: true},
['!build/tree.dart', '!test.unit.dartvm/run']);
runSequence(
'build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
'!build/change_detect.dart', '!test.unit.dartvm/run', function(error) {
// Watch for changes made in the TS and Dart code under "modules" and
// run ts2dart and test change detector generator prior to rerunning the
// tests.
watch('modules/angular2/**', {ignoreInitial: true},
['!build/tree.dart', '!build/change_detect.dart', '!test.unit.dartvm/run']);
// Watch for changes made in Dart code under "modules_dart", then copy it
// to dist and run test change detector generator prior to retunning the
// tests.
watch('modules_dart/**', {ignoreInitial: true},
['build/pure-packages.dart', '!test.unit.dartvm/run']);
});
// Watch for changes made in Dart code under "modules_dart", then copy it
// to dist and run test change detector generator prior to retunning the
// tests.
watch('modules_dart/**', {ignoreInitial: true},
['build/pure-packages.dart', '!build/change_detect.dart', '!test.unit.dartvm/run']);
});
});
gulp.task('!test.unit.dartvm/run',
@ -1008,38 +977,21 @@ gulp.task('static-checks', ['!build.tools'], function(done) {
// distributed in our npm package, and loaded from node_modules by
// the typescript compiler.
// Make sure the typings tests are isolated, by running in a tempdir
// Make sure the two typings tests are isolated, by running this one in a tempdir
var tmpdir = path.join(os.tmpdir(), 'test.typings', new Date().getTime().toString());
gulp.task('!pre.test.typings.layoutNodeModule', function() {
return gulp.src(['dist/js/cjs/angular2/**/*', 'node_modules/rxjs/**/*'], {base: 'dist/js/cjs'})
gulp.task('!pre.test.typings.layoutNodeModule', ['build.js.cjs'], function() {
return gulp.src(['dist/js/cjs/angular2/**/*', 'node_modules/rxjs/**'], {base: 'dist/js/cjs'})
.pipe(gulp.dest(path.join(tmpdir, 'node_modules')));
});
gulp.task('!pre.test.typings.copyDeps', function() {
return gulp.src(
[
'modules/angular2/typings/angular-protractor/*.ts',
'modules/angular2/typings/jasmine/*.ts',
'modules/angular2/typings/selenium-webdriver/*.ts',
],
{base: 'modules/angular2/typings'})
.pipe(gulp.dest(tmpdir));
});
gulp.task('!pre.test.typings.copyTypingsSpec', function() {
return gulp.src(['modules/angular2/examples/**/*.ts']).pipe(gulp.dest(tmpdir));
return gulp.src(['typing_spec/*.ts'], {base: 'typing_spec'}).pipe(gulp.dest(tmpdir));
});
gulp.task('!test.typings',
[
'!pre.test.typings.layoutNodeModule',
'!pre.test.typings.copyTypingsSpec',
'!pre.test.typings.copyDeps'
],
function() {
gulp.task('test.typings',
['!pre.test.typings.layoutNodeModule', '!pre.test.typings.copyTypingsSpec'], function() {
var tsc = require('gulp-typescript');
return gulp.src([tmpdir + '/**/*.ts', '!' + tmpdir + '/node_modules/**/*'])
return gulp.src([tmpdir + '/*.ts'])
.pipe(tsc({
target: 'ES6',
module: 'commonjs',
@ -1050,46 +1002,6 @@ gulp.task('!test.typings',
}));
});
gulp.task('test.typings', ['build.js.cjs'],
function(done) { runSequence('!test.typings', sequenceComplete(done)); });
gulp.task('!build.compiler_cli', function(done) { runTsc('tools/compiler_cli/src', done); });
gulp.task('!clean.compiler_cli', function(done) {
fse.remove(path.join('dist', 'tools', 'compiler_cli', 'test'),
fse.remove(path.join('tools', 'compiler_cli', 'test', 'src', '*.ngfactory.ts'),
fse.remove(path.join('tools', 'compiler_cli', 'test', 'src', 'a',
'*.ngfactory.ts'),
done)));
});
gulp.task('!test.compiler_cli.codegen', function(done) {
try {
require('./dist/tools/compiler_cli/main')
.main("tools/compiler_cli/test")
.then(done)
.catch(function(rej) { done(rej); });
} catch (err) {
done(err);
}
});
gulp.task('!test.compiler_cli.unit',
function(done) { runJasmineTests(['dist/tools/compiler_cli/**/*_spec.js'], done) });
// This task overwrites our careful tsickle-lowered Decorators with normal .js emit.
// So it should only be run after asserting on the .js file content.
gulp.task('!test.compiler_cli.verify_codegen',
function(done) { runTsc('tools/compiler_cli/test', done); });
// End-to-end test for compiler CLI.
// Calls the compiler using its command-line interface, then compiles the app with the codegen.
// TODO(alexeagle): wire up the playground tests with offline compilation, similar to dart.
gulp.task('test.compiler_cli', ['!build.compiler_cli'], function(done) {
runSequence('!clean.compiler_cli', '!test.compiler_cli.codegen', '!test.compiler_cli.unit',
'!test.compiler_cli.verify_codegen', sequenceComplete(done));
});
// -----------------
// orchestrated targets
@ -1133,15 +1045,15 @@ gulp.task('build/pure-packages.dart/angular2', function() {
// Builds all Dart packages, but does not compile them
gulp.task('build/packages.dart', function(done) {
runSequence('lint_protos.dart', 'pubget.dart', 'build/tree.dart', 'build/pure-packages.dart',
runSequence('lint_protos.dart', 'build/tree.dart', 'build/pure-packages.dart',
// Run after 'build/tree.dart' because broccoli clears the dist/dart folder
'!build/pubget.angular2.dart', sequenceComplete(done));
'!build/pubget.angular2.dart', '!build/change_detect.dart', sequenceComplete(done));
});
// Builds and compiles all Dart packages
gulp.task('build.dart', function(done) {
runSequence('build/packages.dart', 'build/pubspec.dart', 'build/analyze.dart',
'build/check.apidocs.dart', sequenceComplete(done));
'build/check.apidocs.dart', 'build.dart.material.css', sequenceComplete(done));
});
@ -1155,31 +1067,30 @@ gulp.task('!build.tools', function() {
var sourcemaps = require('gulp-sourcemaps');
var tsc = require('gulp-typescript');
var stream = gulp.src(['tools/**/*.ts', '!tools/compiler_cli/**'])
var stream = gulp.src(['tools/**/*.ts'])
.pipe(sourcemaps.init())
.pipe(tsc({
target: 'ES5',
module: 'commonjs',
declaration: true,
// Don't use the version of typescript that gulp-typescript depends on
// see https://github.com/ivogabe/gulp-typescript#typescript-version
typescript: require('typescript')
}));
stream =
merge2([stream.js.pipe(gulp.dest('dist/tools')), stream.dts.pipe(gulp.dest('dist/tools'))])
.on('error',
function(error) {
// nodejs doesn't propagate errors from the src stream into the final
// stream so we are
// forwarding the error into the final stream
stream.emit('error', error);
})
.pipe(sourcemaps.write('.'))
.on('end', function() {
var AngularBuilder = require('./dist/tools/broccoli/angular_builder').AngularBuilder;
angularBuilder =
new AngularBuilder({outputPath: 'dist', dartSDK: DART_SDK, logs: logs});
});
}))
.on('error',
function(error) {
// nodejs doesn't propagate errors from the src stream into the final
// stream so we are
// forwarding the error into the final stream
stream.emit('error', error);
})
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/tools'))
.on('end', function() {
var AngularBuilder =
require('./dist/tools/broccoli/angular_builder').AngularBuilder;
angularBuilder =
new AngularBuilder({outputPath: 'dist', dartSDK: DART_SDK, logs: logs});
});
return stream;
});
@ -1201,8 +1112,9 @@ gulp.task('!broccoli.js.prod', () => angularBuilder.rebuildBrowserProdTree({
useBundles: cliArgs.useBundles
}));
gulp.task('build.js.dev', ['build/clean.js'],
function(done) { runSequence('broccoli.js.dev', sequenceComplete(done)); });
gulp.task('build.js.dev', ['build/clean.js'], function(done) {
runSequence('broccoli.js.dev', 'build.css.material', sequenceComplete(done));
});
gulp.task('build.js.prod', ['build.tools'],
function(done) { runSequence('!broccoli.js.prod', sequenceComplete(done)); });
@ -1261,8 +1173,6 @@ gulp.task('!bundle.js.prod', ['build.js.prod'], function() {
bundler.bundle(bundleConfig, HTTP_BUNDLE_CONTENT, './dist/build/http.js', bundlerConfig),
bundler.bundle(bundleConfig, ROUTER_BUNDLE_CONTENT, './dist/build/router.js',
bundlerConfig),
bundler.bundle(bundleConfig, ALT_ROUTER_BUNDLE_CONTENT, './dist/build/alt_router.js',
bundlerConfig),
bundler.bundle(bundleConfig, UPGRADE_BUNDLE_CONTENT, './dist/build/upgrade.js',
bundlerConfig)
]);
@ -1272,8 +1182,7 @@ gulp.task('!bundle.js.prod', ['build.js.prod'], function() {
// minified production build
gulp.task('!bundle.js.min', ['build.js.prod'], function() {
var bundler = require('./tools/build/bundle');
var bundlerConfig =
{sourceMaps: true, minify: true, mangle: false, uglify: {compress: {keep_fnames: true}}};
var bundlerConfig = {sourceMaps: true, minify: true};
return bundler.bundle(bundleConfig, NG2_BUNDLE_CONTENT, './dist/build/angular2.min.js',
bundlerConfig)
@ -1283,8 +1192,6 @@ gulp.task('!bundle.js.min', ['build.js.prod'], function() {
bundlerConfig),
bundler.bundle(bundleConfig, ROUTER_BUNDLE_CONTENT, './dist/build/router.min.js',
bundlerConfig),
bundler.bundle(bundleConfig, ALT_ROUTER_BUNDLE_CONTENT, './dist/build/alt_router.min.js',
bundlerConfig),
bundler.bundle(bundleConfig, UPGRADE_BUNDLE_CONTENT, './dist/build/upgrade.min.js',
bundlerConfig)
]);
@ -1307,8 +1214,6 @@ gulp.task('!bundle.js.dev', ['build.js.dev'], function() {
bundlerConfig),
bundler.bundle(devBundleConfig, ROUTER_BUNDLE_CONTENT, './dist/build/router.dev.js',
bundlerConfig),
bundler.bundle(devBundleConfig, ALT_ROUTER_BUNDLE_CONTENT,
'./dist/build/alt_router.dev.js', bundlerConfig),
bundler.bundle(devBundleConfig, UPGRADE_BUNDLE_CONTENT, './dist/build/upgrade.dev.js',
bundlerConfig)
]);
@ -1429,7 +1334,6 @@ gulp.task('!bundle.js.prod.deps', ['!bundle.js.prod'], function() {
return merge2(bundler.modify(['dist/build/angular2.js'], 'angular2.js'),
bundler.modify(['dist/build/http.js'], 'http.js'),
bundler.modify(['dist/build/router.js'], 'router.js'),
bundler.modify(['dist/build/alt_router.js'], 'alt_router.js'),
bundler.modify(['dist/build/upgrade.js'], 'upgrade.js'))
.pipe(gulp.dest('dist/js/bundle'));
});
@ -1441,7 +1345,6 @@ gulp.task('!bundle.js.min.deps', ['!bundle.js.min'], function() {
return merge2(bundler.modify(['dist/build/angular2.min.js'], 'angular2.min.js'),
bundler.modify(['dist/build/http.min.js'], 'http.min.js'),
bundler.modify(['dist/build/router.min.js'], 'router.min.js'),
bundler.modify(['dist/build/alt_router.min.js'], 'alt_router.min.js'),
bundler.modify(['dist/build/upgrade.min.js'], 'upgrade.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js/bundle'));
@ -1452,7 +1355,7 @@ gulp.task('!bundle.ng.polyfills', ['clean'],
var JS_DEV_DEPS = [
licenseWrap('node_modules/zone.js/LICENSE', true),
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
licenseWrap('node_modules/reflect-metadata/LICENSE', true),
'node_modules/reflect-metadata/Reflect.js'
@ -1473,7 +1376,6 @@ gulp.task('!bundle.js.dev.deps', ['!bundle.js.dev'], function() {
return merge2(bundler.modify(['dist/build/angular2.dev.js'], 'angular2.dev.js'),
bundler.modify(['dist/build/http.dev.js'], 'http.dev.js'),
bundler.modify(['dist/build/router.dev.js'], 'router.dev.js'),
bundler.modify(['dist/build/alt_router.dev.js'], 'alt_router.dev.js'),
bundler.modify(['dist/build/upgrade.dev.js'], 'upgrade.dev.js'))
.pipe(gulp.dest('dist/js/bundle'));
});
@ -1540,7 +1442,70 @@ gulp.task('gen_protos.dart', function(done) {
done);
});
// change detection codegen
gulp.task('build.change_detect.dart', function(done) {
return runSequence('build/packages.dart', '!build/pubget.angular2.dart',
'!build/change_detect.dart', done);
});
gulp.task('!build/change_detect.dart', function(done) {
var fs = require('fs');
var spawn = require('child_process').spawn;
var changeDetectDir = path.join(CONFIG.dest.dart, 'angular2/test/core/change_detection/');
var srcDir = path.join(changeDetectDir, 'generator');
var destDir = path.join(changeDetectDir, 'generated');
var dartStream = fs.createWriteStream(path.join(destDir, 'change_detector_classes.dart'));
var genMain = path.join(srcDir, 'gen_change_detectors.dart');
var proc = spawn(DART_SDK.VM, [genMain], {stdio: ['ignore', 'pipe', 'inherit']});
proc.on('error', function(code) {
done(new Error('Failed while generating change detector classes. Please run manually: ' +
DART_SDK.VM + ' ' + dartArgs.join(' ')));
});
proc.on('close', function() {
dartStream.close();
done();
});
proc.stdout.pipe(dartStream);
});
// ------------
// angular material testing rules
gulp.task('build.css.material', function() {
var autoprefixer = require('gulp-autoprefixer');
var sass = require('gulp-sass');
return gulp.src('modules/*/src/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest(CONFIG.dest.js.prod.es5))
.pipe(gulp.dest(CONFIG.dest.js.dev.es5))
.pipe(gulp.dest(CONFIG.dest.js.dart2js + '/examples/packages'));
});
gulp.task('build.js.material', function(done) {
runSequence('build.js.dev', 'build.css.material', sequenceComplete(done));
});
gulp.task('build.dart2js.material', function(done) {
runSequence('build.dart', 'build.css.material', sequenceComplete(done));
});
gulp.task('build.dart.material.css', function() {
var autoprefixer = require('gulp-autoprefixer');
var sass = require('gulp-sass');
return gulp.src('dist/dart/angular2_material/src/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest('dist/dart/angular2_material/lib/src'));
});
gulp.task('build.dart.material', ['build/packages.dart'], function(done) {
runSequence('build/packages.dart', 'build.dart.material.css', sequenceComplete(done));
});
gulp.task('cleanup.builder', function() { return angularBuilder.cleanup(); });
@ -1579,16 +1544,13 @@ process.on('beforeExit', function() {
var firstTask = true;
gulp.on('task_start', (e) => {
if (firstTask) {
firstTask = false;
analytics.buildSuccess('gulp <startup>', process.uptime() * 1000);
}
analytics.buildStart('gulp ' + e.task);
analytics.buildStart('gulp ' + e.task)
});
gulp.on('task_stop', (e) => { analytics.buildSuccess('gulp ' + e.task, e.duration * 1000); });
gulp.on('task_err', (e) => { analytics.buildError('gulp ' + e.task, e.duration * 1000); });
gulp.on('task_stop', (e) => {analytics.buildSuccess('gulp ' + e.task, e.duration * 1000)});
gulp.on('task_err', (e) => {analytics.buildError('gulp ' + e.task, e.duration * 1000)});

View File

@ -11,37 +11,28 @@ module.exports = function(config) {
files: [
// Sources and specs.
// Loaded through the System loader, in `test-main.js`.
{pattern: 'dist/all/@angular/**/*.js', included: false, watched: true},
{pattern: 'dist/all/angular2/**/*.js', included: false, watched: true},
{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',
'node_modules/zone.js/dist/zone.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',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
// Including systemjs because it defines `__eval`, which produces correct stack traces.
'shims_for_IE.js',
'modules/angular2/src/testing/shims_for_IE.js',
'node_modules/systemjs/dist/system.src.js',
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
'node_modules/reflect-metadata/Reflect.js',
'tools/build/file2modulename.js',
'test-main.js',
{pattern: 'dist/all/empty.*', included: false, watched: false},
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
{pattern: 'modules/@angular/platform-browser-dynamic/test/browser/static_assets/**', included: false, watched: false}
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
],
exclude: [
'dist/all/@angular/**/e2e_test/**',
'dist/all/@angular/examples/**',
'dist/all/angular1_router.js',
'dist/all/@angular/platform-browser/testing/e2e_util.js'
],
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/js/dev/es5/angular2/examples/**', 'dist/angular1_router.js'],
customLaunchers: browserProvidersConf.customLaunchers,
@ -62,7 +53,6 @@ module.exports = function(config) {
reporters: ['internal-angular'],
sauceLabs: {
testName: 'Angular2',
retryLimit: 3,
startConnect: false,
recordVideo: false,
recordScreenshots: false,
@ -77,23 +67,19 @@ module.exports = function(config) {
browserStack: {
project: 'Angular2',
startTunnel: false,
retryLimit: 3,
retryLimit: 1,
timeout: 600,
pollingTimeout: 10000
},
browsers: ['Chrome'],
port: 9876,
captureTimeout: 60000,
browserDisconnectTimeout : 60000,
browserDisconnectTolerance : 3,
browserNoActivityTimeout : 60000,
port: 9876
});
if (process.env.TRAVIS) {
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
if (process.env.CI_MODE.startsWith('saucelabs')) {
if (process.env.MODE.startsWith('saucelabs')) {
config.sauceLabs.build = buildId;
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
@ -103,7 +89,7 @@ module.exports = function(config) {
config.transports = ['polling'];
}
if (process.env.CI_MODE.startsWith('browserstack')) {
if (process.env.MODE.startsWith('browserstack')) {
config.browserStack.build = buildId;
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
}

View File

@ -1 +0,0 @@
export 'index.dart';

View File

@ -1,5 +0,0 @@
export * from './src/pipes';
export * from './src/directives';
export * from './src/forms';
export * from './src/common_directives';
export * from './src/location';

View File

@ -1,13 +0,0 @@
{
"name": "@angular/common",
"version": "$$ANGULAR_VERSION$$",
"description": "",
"main": "index.js",
"jsnext:main": "esm/index.js",
"typings": "index.d.ts",
"author": "angular",
"license": "MIT",
"peerDependencies": {
"@angular/core": "$$ANGULAR_VERSION$$"
}
}

View File

@ -1,17 +0,0 @@
export default {
entry: '../../../dist/packages-dist/common/esm/index.js',
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
format: 'umd',
moduleName: 'ng.common',
globals: {
'@angular/core': 'ng.core',
'rxjs/Subject': 'Rx',
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
'rxjs/Observable': 'Rx'
},
plugins: [
// nodeResolve({ jsnext: true, main: true }),
]
}

View File

@ -1,148 +0,0 @@
import {
Directive,
ViewContainerRef,
TemplateRef,
ContentChildren,
QueryList,
Attribute,
AfterContentInit,
Input
} from '@angular/core';
import {isPresent, NumberWrapper} from '../../src/facade/lang';
import {Map} from '../../src/facade/collection';
import {SwitchView} from './ng_switch';
const _CATEGORY_DEFAULT = 'other';
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
/**
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
*
* To use this directive, you must provide an extension of `NgLocalization` that maps values to
* category names. You then define a container element that sets the `[ngPlural]` attribute to a
* switch expression.
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
* expression.
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
* matches the switch expression exactly.
* - Otherwise, the view will be treated as a "category match", and will only display if exact
* value matches aren't found and the value maps to its category using the `getPluralCategory`
* function provided.
*
* If no matching views are found for a switch expression, inner elements marked
* `[ngPluralCase]="other"` will be displayed.
*
* ```typescript
* class MyLocalization extends NgLocalization {
* getPluralCategory(value: any) {
* if(value < 5) {
* return 'few';
* }
* }
* }
*
* @Component({
* selector: 'app',
* providers: [provide(NgLocalization, {useClass: MyLocalization})]
* })
* @View({
* template: `
* <p>Value = {{value}}</p>
* <button (click)="inc()">Increment</button>
*
* <div [ngPlural]="value">
* <template ngPluralCase="=0">there is nothing</template>
* <template ngPluralCase="=1">there is one</template>
* <template ngPluralCase="few">there are a few</template>
* <template ngPluralCase="other">there is some number</template>
* </div>
* `,
* directives: [NgPlural, NgPluralCase]
* })
* export class App {
* value = 'init';
*
* inc() {
* this.value = this.value === 'init' ? 0 : this.value + 1;
* }
* }
*
* ```
*/
@Directive({selector: '[ngPluralCase]'})
export class NgPluralCase {
/** @internal */
_view: SwitchView;
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
viewContainer: ViewContainerRef) {
this._view = new SwitchView(viewContainer, template);
}
}
@Directive({selector: '[ngPlural]'})
export class NgPlural implements AfterContentInit {
private _switchValue: number;
private _activeView: SwitchView;
private _caseViews = new Map<any, SwitchView>();
@ContentChildren(NgPluralCase) cases: QueryList<NgPluralCase> = null;
constructor(private _localization: NgLocalization) {}
@Input()
set ngPlural(value: number) {
this._switchValue = value;
this._updateView();
}
ngAfterContentInit() {
this.cases.forEach((pluralCase: NgPluralCase): void => {
this._caseViews.set(this._formatValue(pluralCase), pluralCase._view);
});
this._updateView();
}
/** @internal */
_updateView(): void {
this._clearViews();
var view: SwitchView = this._caseViews.get(this._switchValue);
if (!isPresent(view)) view = this._getCategoryView(this._switchValue);
this._activateView(view);
}
/** @internal */
_clearViews() {
if (isPresent(this._activeView)) this._activeView.destroy();
}
/** @internal */
_activateView(view: SwitchView) {
if (!isPresent(view)) return;
this._activeView = view;
this._activeView.create();
}
/** @internal */
_getCategoryView(value: number): SwitchView {
var category: string = this._localization.getPluralCategory(value);
var categoryView: SwitchView = this._caseViews.get(category);
return isPresent(categoryView) ? categoryView : this._caseViews.get(_CATEGORY_DEFAULT);
}
/** @internal */
_isValueView(pluralCase: NgPluralCase): boolean { return pluralCase.value[0] === "="; }
/** @internal */
_formatValue(pluralCase: NgPluralCase): any {
return this._isValueView(pluralCase) ? this._stripValue(pluralCase.value) : pluralCase.value;
}
/** @internal */
_stripValue(value: string): number { return NumberWrapper.parseInt(value.substring(1), 10); }
}

View File

@ -1,26 +0,0 @@
import {Directive, Input, ViewContainerRef, ViewRef, TemplateRef} from '@angular/core';
import {isPresent} from '../../src/facade/lang';
/**
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
*
* ### Syntax
* - `<template [ngTemplateOutlet]="templateRefExpression"></template>`
*/
@Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet {
private _insertedViewRef: ViewRef;
constructor(private _viewContainerRef: ViewContainerRef) {}
@Input()
set ngTemplateOutlet(templateRef: TemplateRef<Object>) {
if (isPresent(this._insertedViewRef)) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._insertedViewRef));
}
if (isPresent(templateRef)) {
this._insertedViewRef = this._viewContainerRef.createEmbeddedView(templateRef);
}
}
}

View File

@ -1 +0,0 @@
../../facade/src

View File

@ -1,18 +0,0 @@
import {AbstractControl} from '../model';
import {Validator, ValidatorFn, AsyncValidatorFn} from './validators';
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
if ((<Validator>validator).validate !== undefined) {
return (c: AbstractControl) => (<Validator>validator).validate(c);
} else {
return <ValidatorFn>validator;
}
}
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
if ((<Validator>validator).validate !== undefined) {
return (c: AbstractControl) => Promise.resolve((<Validator>validator).validate(c));
} else {
return <AsyncValidatorFn>validator;
}
}

View File

@ -1,139 +0,0 @@
import {
Directive,
Renderer,
forwardRef,
Provider,
ElementRef,
Input,
Host,
OnDestroy,
Optional
} from '@angular/core';
import {
StringWrapper,
isPrimitive,
isPresent,
isBlank,
looseIdentical
} from '../../../src/facade/lang';
import {MapWrapper} from '../../../src/facade/collection';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
export const SELECT_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectControlValueAccessor),
multi: true
};
function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (!isPrimitive(value)) value = "Object";
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
}
function _extractId(valueString: string): string {
return valueString.split(":")[0];
}
/**
* The accessor for writing a value and listening to changes on a select element.
*
* Note: We have to listen to the 'change' event because 'input' events aren't fired
* for selects in Firefox and IE:
* https://bugzilla.mozilla.org/show_bug.cgi?id=1024350
* https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/
*
*/
@Directive({
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
host: {'(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
providers: [SELECT_VALUE_ACCESSOR]
})
export class SelectControlValueAccessor implements ControlValueAccessor {
value: any;
/** @internal */
_optionMap: Map<string, any> = new Map<string, any>();
/** @internal */
_idCounter: number = 0;
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
this.value = value;
var valueString = _buildValueString(this._getOptionId(value), value);
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
}
registerOnChange(fn: (value: any) => any): void {
this.onChange = (valueString: string) => { fn(this._getOptionValue(valueString)); };
}
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
/** @internal */
_registerOption(): string { return (this._idCounter++).toString(); }
/** @internal */
_getOptionId(value: any): string {
for (let id of MapWrapper.keys(this._optionMap)) {
if (looseIdentical(this._optionMap.get(id), value)) return id;
}
return null;
}
/** @internal */
_getOptionValue(valueString: string): any {
let value = this._optionMap.get(_extractId(valueString));
return isPresent(value) ? value : valueString;
}
}
/**
* Marks `<option>` as dynamic, so Angular can be notified when options change.
*
* ### Example
*
* ```
* <select ngControl="city">
* <option *ngFor="let c of cities" [value]="c"></option>
* </select>
* ```
*/
@Directive({selector: 'option'})
export class NgSelectOption implements OnDestroy {
id: string;
constructor(private _element: ElementRef, private _renderer: Renderer,
@Optional() @Host() private _select: SelectControlValueAccessor) {
if (isPresent(this._select)) this.id = this._select._registerOption();
}
@Input('ngValue')
set ngValue(value: any) {
if (this._select == null) return;
this._select._optionMap.set(this.id, value);
this._setElementValue(_buildValueString(this.id, value));
this._select.writeValue(this._select.value);
}
@Input('value')
set value(value: any) {
this._setElementValue(value);
if (isPresent(this._select)) this._select.writeValue(this._select.value);
}
/** @internal */
_setElementValue(value: string): void {
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
}
ngOnDestroy() {
if (isPresent(this._select)) {
this._select._optionMap.delete(this.id);
this._select.writeValue(this._select.value);
}
}
}

View File

@ -1,5 +0,0 @@
export * from './location/platform_location';
export * from './location/location_strategy';
export * from './location/hash_location_strategy';
export * from './location/path_location_strategy';
export * from './location/location';

View File

@ -1,135 +0,0 @@
import {
beforeEachProviders,
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xit,
} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
import {Component, Injectable, provide} from '@angular/core';
import {NgPlural, NgPluralCase, NgLocalization} from '@angular/common';
export function main() {
describe('switch', () => {
beforeEachProviders(() => [provide(NgLocalization, {useClass: TestLocalizationMap})]);
it('should display the template according to the exact value',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 0;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
fixture.debugElement.componentInstance.switchValue = 1;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
async.done();
});
}));
it('should display the template according to the category',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template =
'<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 2;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have a few messages.');
fixture.debugElement.componentInstance.switchValue = 8;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have many messages.');
async.done();
});
}));
it('should default to other when no matches are found',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template =
'<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="other"><li>default message.</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 100;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('default message.');
async.done();
});
}));
it('should prioritize value matches over category matches',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template =
'<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="=2">you have two messages.</template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 2;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have two messages.');
fixture.debugElement.componentInstance.switchValue = 3;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have a few messages.');
async.done();
});
}));
});
}
@Injectable()
export class TestLocalizationMap extends NgLocalization {
getPluralCategory(value: number): string {
if (value > 1 && value < 4) {
return 'few';
} else if (value >= 4 && value < 10) {
return 'many';
} else {
return 'other';
}
}
}
@Component({selector: 'test-cmp', directives: [NgPlural, NgPluralCase], template: ''})
class TestComponent {
switchValue: number;
constructor() { this.switchValue = null; }
}

View File

@ -1,110 +0,0 @@
import {
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xit,
} from '@angular/core/testing/testing_internal';
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
import {Component, Directive, TemplateRef, ContentChildren, QueryList} from '@angular/core';
import {NgTemplateOutlet} from '@angular/common';
export function main() {
describe('insert', () => {
it('should do nothing if templateRef is null',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = `<template [ngTemplateOutlet]="null"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
async.done();
});
}));
it('should insert content specified by TemplateRef',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
async.done();
});
}));
it('should clear content if TemplateRef becomes null',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
fixture.componentInstance.currentTplRef = null;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
async.done();
});
}));
it('should swap content if TemplateRef changes',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = `<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
fixture.componentInstance.currentTplRef = refs.tplRefs.last;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('bar');
async.done();
});
}));
});
}
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
class CaptureTplRefs {
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
}
@Component({selector: 'test-cmp', directives: [NgTemplateOutlet, CaptureTplRefs], template: ''})
class TestComponent {
currentTplRef: TemplateRef<any>;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +0,0 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach
} from '@angular/core/testing/testing_internal';
import {browserDetection} from '@angular/platform-browser/testing';
import {DatePipe} from '@angular/common';
import {DateWrapper} from '../../src/facade/lang';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
export function main() {
describe("DatePipe", () => {
var date;
var pipe;
beforeEach(() => {
date = DateWrapper.create(2015, 6, 15, 21, 43, 11);
pipe = new DatePipe();
});
it('should be marked as pure',
() => { expect(new PipeResolver().resolve(DatePipe).pure).toEqual(true); });
describe("supports", () => {
it("should support date", () => { expect(pipe.supports(date)).toBe(true); });
it("should support int", () => { expect(pipe.supports(123456789)).toBe(true); });
it("should not support other objects", () => {
expect(pipe.supports(new Object())).toBe(false);
expect(pipe.supports(null)).toBe(false);
});
});
// TODO(mlaval): enable tests when Intl API is no longer used, see
// https://github.com/angular/angular/issues/3333
if (browserDetection.supportsIntlApi) {
describe("transform", () => {
it('should format each component correctly', () => {
expect(pipe.transform(date, 'y')).toEqual('2015');
expect(pipe.transform(date, 'yy')).toEqual('15');
expect(pipe.transform(date, 'M')).toEqual('6');
expect(pipe.transform(date, 'MM')).toEqual('06');
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
expect(pipe.transform(date, 'MMMM')).toEqual('June');
expect(pipe.transform(date, 'd')).toEqual('15');
expect(pipe.transform(date, 'E')).toEqual('Mon');
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
expect(pipe.transform(date, 'H')).toEqual('21');
expect(pipe.transform(date, 'j')).toEqual('9 PM');
expect(pipe.transform(date, 'm')).toEqual('43');
expect(pipe.transform(date, 's')).toEqual('11');
});
it('should format common multi component patterns', () => {
expect(pipe.transform(date, 'yMEd')).toEqual('Mon, 6/15/2015');
expect(pipe.transform(date, 'MEd')).toEqual('Mon, 6/15');
expect(pipe.transform(date, 'MMMd')).toEqual('Jun 15');
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, 'jms')).toEqual('9:43:11 PM');
expect(pipe.transform(date, 'ms')).toEqual('43:11');
});
it('should format with pattern aliases', () => {
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:43:11 PM');
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:43 PM');
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
expect(pipe.transform(date, 'mediumTime')).toEqual('9:43:11 PM');
expect(pipe.transform(date, 'shortTime')).toEqual('9:43 PM');
});
});
}
});
}

View File

@ -1,71 +0,0 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
inject,
} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
import {ReplacePipe} from '@angular/common';
import {RegExpWrapper, StringJoiner} from '../../src/facade/lang';
export function main() {
describe("ReplacePipe", () => {
var someNumber: number;
var str;
var pipe;
beforeEach(() => {
someNumber = 42;
str = 'Douglas Adams';
pipe = new ReplacePipe();
});
describe("transform", () => {
it("should not support input other than strings and numbers", () => {
expect(() => pipe.transform({}, "Douglas", "Hugh")).toThrow();
expect(() => pipe.transform([1, 2, 3], "Douglas", "Hugh")).toThrow();
});
it("should not support patterns other than strings and regular expressions", () => {
expect(() => pipe.transform(str, {}, "Hugh")).toThrow();
expect(() => pipe.transform(str, null, "Hugh")).toThrow();
expect(() => pipe.transform(str, 123, "Hugh")).toThrow();
});
it("should not support replacements other than strings and functions", () => {
expect(() => pipe.transform(str, "Douglas", {})).toThrow();
expect(() => pipe.transform(str, "Douglas", null)).toThrow();
expect(() => pipe.transform(str, "Douglas", 123)).toThrow();
});
it("should return a new string with the pattern replaced", () => {
var result1 = pipe.transform(str, "Douglas", "Hugh");
var result2 = pipe.transform(str, RegExpWrapper.create("a"), "_");
var result3 = pipe.transform(str, RegExpWrapper.create("a", "i"), "_");
var f = (x => { return "Adams!"; });
var result4 = pipe.transform(str, "Adams", f);
var result5 = pipe.transform(someNumber, "2", "4");
expect(result1).toEqual("Hugh Adams");
expect(result2).toEqual("Dougl_s Ad_ms");
expect(result3).toEqual("Dougl_s _d_ms");
expect(result4).toEqual("Douglas Adams!");
expect(result5).toEqual("44");
});
});
});
}

View File

@ -1,2 +0,0 @@
export {MockLocationStrategy} from './testing/mock_location_strategy';
export {SpyLocation} from './testing/location_mock';

View File

@ -1,26 +0,0 @@
{
"angularCompilerOptions": {
"skipTemplateCodegen": true
},
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "es2015",
"moduleResolution": "node",
"outDir": "../../../dist/packages-dist/common/esm",
"paths": {
"@angular/core": ["../../../dist/packages-dist/core"]
},
"rootDir": ".",
"sourceMap": true,
"inlineSources": true,
"target": "es2015"
},
"files": [
"index.ts",
"testing.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts"
]
}

View File

@ -1,30 +0,0 @@
{
"angularCompilerOptions": {
"skipTemplateCodegen": true
},
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../../../dist/packages-dist/common/",
"paths": {
"@angular/core": ["../../../dist/packages-dist/core/"]
},
"rootDir": ".",
"sourceMap": true,
"inlineSources": true,
"target": "es5"
},
"files": [
"index.ts",
"testing.ts",
"../typings/es6-collections/es6-collections.d.ts",
"../typings/es6-promise/es6-promise.d.ts",
"../manual_typings/globals.d.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts"
]
}

View File

@ -1,37 +0,0 @@
/**
* @module
* @description
* Starting point to import all compiler APIs.
*/
export {ElementSchemaRegistry} from './src/schema/element_schema_registry';
export {
COMPILER_PROVIDERS,
TEMPLATE_TRANSFORMS,
CompilerConfig,
RenderTypes,
UrlResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
createOfflineCompileUrlResolver,
XHR,
ViewResolver,
DirectiveResolver,
PipeResolver,
SourceModule,
NormalizedComponentWithViewDirectives,
OfflineCompiler,
CompileMetadataWithIdentifier,
CompileMetadataWithType,
CompileIdentifierMetadata,
CompileDiDependencyMetadata,
CompileProviderMetadata,
CompileFactoryMetadata,
CompileTokenMetadata,
CompileTypeMetadata,
CompileQueryMetadata,
CompileTemplateMetadata,
CompileDirectiveMetadata,
CompilePipeMetadata
} from './src/compiler';
export * from './src/template_ast';
export * from './private_export';

View File

@ -1 +0,0 @@
export '../core/private_export.dart';

View File

@ -1,60 +0,0 @@
import {__core_private__ as r, __core_private_types__ as t} from '@angular/core';
export var isDefaultChangeDetectionStrategy: typeof t.isDefaultChangeDetectionStrategy =
r.isDefaultChangeDetectionStrategy;
export type ChangeDetectorState = t.ChangeDetectorState;
export var ChangeDetectorState: typeof t.ChangeDetectorState = r.ChangeDetectorState;
export var CHANGE_DETECTION_STRATEGY_VALUES: typeof t.CHANGE_DETECTION_STRATEGY_VALUES =
r.CHANGE_DETECTION_STRATEGY_VALUES;
export var constructDependencies: typeof t.constructDependencies = r.constructDependencies;
export type LifecycleHooks = t.LifecycleHooks;
export var LifecycleHooks: typeof t.LifecycleHooks = r.LifecycleHooks;
export var LIFECYCLE_HOOKS_VALUES: typeof t.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES;
export type ReflectorReader = t.ReflectorReader;
export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader;
export var ReflectorComponentResolver: typeof t.ReflectorComponentResolver =
r.ReflectorComponentResolver;
export type AppElement = t.AppElement;
export var AppElement: typeof t.AppElement = r.AppElement;
export var AppView: typeof t.AppView = r.AppView;
export type DebugAppView<T> = t.DebugAppView<T>;
export var DebugAppView: typeof t.DebugAppView = r.DebugAppView;
export type ViewType = t.ViewType;
export var ViewType: typeof t.ViewType = r.ViewType;
export var MAX_INTERPOLATION_VALUES: typeof t.MAX_INTERPOLATION_VALUES = r.MAX_INTERPOLATION_VALUES;
export var checkBinding: typeof t.checkBinding = r.checkBinding;
export var flattenNestedViewRenderNodes: typeof t.flattenNestedViewRenderNodes =
r.flattenNestedViewRenderNodes;
export var interpolate: typeof t.interpolate = r.interpolate;
export var ViewUtils: typeof t.ViewUtils = r.ViewUtils;
export var VIEW_ENCAPSULATION_VALUES: typeof t.VIEW_ENCAPSULATION_VALUES =
r.VIEW_ENCAPSULATION_VALUES;
export var DebugContext: typeof t.DebugContext = r.DebugContext;
export var StaticNodeDebugInfo: typeof t.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
export var devModeEqual: typeof t.devModeEqual = r.devModeEqual;
export var uninitialized: typeof t.uninitialized = r.uninitialized;
export var ValueUnwrapper: typeof t.ValueUnwrapper = r.ValueUnwrapper;
export var TemplateRef_: typeof t.TemplateRef_ = r.TemplateRef_;
export type RenderDebugInfo = t.RenderDebugInfo;
export var RenderDebugInfo: typeof t.RenderDebugInfo = r.RenderDebugInfo;
export var SecurityContext: typeof t.SecurityContext = r.SecurityContext;
export type SecurityContext = t.SecurityContext;
export var SanitizationService: typeof t.SanitizationService = r.SanitizationService;
export type SanitizationService = t.SanitizationService;
export var createProvider: typeof t.createProvider = r.createProvider;
export var isProviderLiteral: typeof t.isProviderLiteral = r.isProviderLiteral;
export var EMPTY_ARRAY: typeof t.EMPTY_ARRAY = r.EMPTY_ARRAY;
export var EMPTY_MAP: typeof t.EMPTY_MAP = r.EMPTY_MAP;
export var pureProxy1: typeof t.pureProxy1 = r.pureProxy1;
export var pureProxy2: typeof t.pureProxy2 = r.pureProxy2;
export var pureProxy3: typeof t.pureProxy3 = r.pureProxy3;
export var pureProxy4: typeof t.pureProxy4 = r.pureProxy4;
export var pureProxy5: typeof t.pureProxy5 = r.pureProxy5;
export var pureProxy6: typeof t.pureProxy6 = r.pureProxy6;
export var pureProxy7: typeof t.pureProxy7 = r.pureProxy7;
export var pureProxy8: typeof t.pureProxy8 = r.pureProxy8;
export var pureProxy9: typeof t.pureProxy9 = r.pureProxy9;
export var pureProxy10: typeof t.pureProxy10 = r.pureProxy10;
export var castByValue: typeof t.castByValue = r.castByValue;
export type Console = t.Console;
export var Console: typeof t.Console = r.Console;

View File

@ -1 +0,0 @@
export * from './compiler';

View File

@ -1,13 +0,0 @@
{
"name": "@angular/compiler",
"version": "$$ANGULAR_VERSION$$",
"description": "",
"main": "index.js",
"jsnext:main": "esm/index.js",
"typings": "index.d.ts",
"author": "angular",
"license": "MIT",
"peerDependencies": {
"@angular/core": "$$ANGULAR_VERSION$$"
}
}

View File

@ -1 +0,0 @@
export './src/core/change_detection/constants.dart' show SelectorMatcher, CssSelector;

View File

@ -1,16 +0,0 @@
import * as selector from './src/selector';
import * as pathUtil from './src/output/path_util';
export namespace __compiler_private__ {
export type SelectorMatcher = selector.SelectorMatcher;
export var SelectorMatcher = selector.SelectorMatcher;
export type CssSelector = selector.CssSelector;
export var CssSelector = selector.CssSelector;
export type AssetUrl = pathUtil.AssetUrl;
export var AssetUrl = pathUtil.AssetUrl;
export type ImportGenerator = pathUtil.ImportGenerator;
export var ImportGenerator = pathUtil.ImportGenerator;
}

View File

@ -1,17 +0,0 @@
export default {
entry: '../../../dist/packages-dist/compiler/esm/index.js',
dest: '../../../dist/packages-dist/compiler/esm/compiler.umd.js',
format: 'umd',
moduleName: 'ng.compiler',
globals: {
'@angular/core': 'ng.core',
'rxjs/Subject': 'Rx',
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
'rxjs/Observable': 'Rx'
},
plugins: [
// nodeResolve({ jsnext: true, main: true }),
]
}

View File

@ -1,3 +0,0 @@
library angular2.core.util.asserions;
void assertArrayOfStrings(String identifier, Object value) {}

View File

@ -1,16 +0,0 @@
import {isArray, isString, isBlank, assertionsEnabled} from '../src/facade/lang';
import {BaseException} from '../src/facade/exceptions';
export function assertArrayOfStrings(identifier: string, value: any) {
if (!assertionsEnabled() || isBlank(value)) {
return;
}
if (!isArray(value)) {
throw new BaseException(`Expected '${identifier}' to be an array of strings.`);
}
for (var i = 0; i < value.length; i += 1) {
if (!isString(value[i])) {
throw new BaseException(`Expected '${identifier}' to be an array of strings.`);
}
}
}

View File

@ -1,64 +0,0 @@
export const $EOF = /*@ts2dart_const*/ 0;
export const $TAB = /*@ts2dart_const*/ 9;
export const $LF = /*@ts2dart_const*/ 10;
export const $VTAB = /*@ts2dart_const*/ 11;
export const $FF = /*@ts2dart_const*/ 12;
export const $CR = /*@ts2dart_const*/ 13;
export const $SPACE = /*@ts2dart_const*/ 32;
export const $BANG = /*@ts2dart_const*/ 33;
export const $DQ = /*@ts2dart_const*/ 34;
export const $HASH = /*@ts2dart_const*/ 35;
export const $$ = /*@ts2dart_const*/ 36;
export const $PERCENT = /*@ts2dart_const*/ 37;
export const $AMPERSAND = /*@ts2dart_const*/ 38;
export const $SQ = /*@ts2dart_const*/ 39;
export const $LPAREN = /*@ts2dart_const*/ 40;
export const $RPAREN = /*@ts2dart_const*/ 41;
export const $STAR = /*@ts2dart_const*/ 42;
export const $PLUS = /*@ts2dart_const*/ 43;
export const $COMMA = /*@ts2dart_const*/ 44;
export const $MINUS = /*@ts2dart_const*/ 45;
export const $PERIOD = /*@ts2dart_const*/ 46;
export const $SLASH = /*@ts2dart_const*/ 47;
export const $COLON = /*@ts2dart_const*/ 58;
export const $SEMICOLON = /*@ts2dart_const*/ 59;
export const $LT = /*@ts2dart_const*/ 60;
export const $EQ = /*@ts2dart_const*/ 61;
export const $GT = /*@ts2dart_const*/ 62;
export const $QUESTION = /*@ts2dart_const*/ 63;
export const $0 = /*@ts2dart_const*/ 48;
export const $9 = /*@ts2dart_const*/ 57;
export const $A = /*@ts2dart_const*/ 65;
export const $E = /*@ts2dart_const*/ 69;
export const $Z = /*@ts2dart_const*/ 90;
export const $LBRACKET = /*@ts2dart_const*/ 91;
export const $BACKSLASH = /*@ts2dart_const*/ 92;
export const $RBRACKET = /*@ts2dart_const*/ 93;
export const $CARET = /*@ts2dart_const*/ 94;
export const $_ = /*@ts2dart_const*/ 95;
export const $a = /*@ts2dart_const*/ 97;
export const $e = /*@ts2dart_const*/ 101;
export const $f = /*@ts2dart_const*/ 102;
export const $n = /*@ts2dart_const*/ 110;
export const $r = /*@ts2dart_const*/ 114;
export const $t = /*@ts2dart_const*/ 116;
export const $u = /*@ts2dart_const*/ 117;
export const $v = /*@ts2dart_const*/ 118;
export const $z = /*@ts2dart_const*/ 122;
export const $LBRACE = /*@ts2dart_const*/ 123;
export const $BAR = /*@ts2dart_const*/ 124;
export const $RBRACE = /*@ts2dart_const*/ 125;
export const $NBSP = /*@ts2dart_const*/ 160;
export const $PIPE = /*@ts2dart_const*/ 124;
export const $TILDA = /*@ts2dart_const*/ 126;
export const $AT = /*@ts2dart_const*/ 64;
export function isWhitespace(code: number): boolean {
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
}

View File

@ -1,62 +0,0 @@
import {ComponentResolver, Type} from '@angular/core';
import {assertionsEnabled} from '../src/facade/lang';
export * from './template_ast';
export {TEMPLATE_TRANSFORMS} from './template_parser';
export {CompilerConfig, RenderTypes} from './config';
export * from './compile_metadata';
export * from './offline_compiler';
export {RuntimeCompiler} from './runtime_compiler';
export * from './url_resolver';
export * from './xhr';
export {ViewResolver} from './view_resolver';
export {DirectiveResolver} from './directive_resolver';
export {PipeResolver} from './pipe_resolver';
import {TemplateParser} from './template_parser';
import {HtmlParser} from './html_parser';
import {DirectiveNormalizer} from './directive_normalizer';
import {CompileMetadataResolver} from './metadata_resolver';
import {StyleCompiler} from './style_compiler';
import {ViewCompiler} from './view_compiler/view_compiler';
import {CompilerConfig} from './config';
import {RuntimeCompiler} from './runtime_compiler';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from './url_resolver';
import {Parser} from './expression_parser/parser';
import {Lexer} from './expression_parser/lexer';
import {ViewResolver} from './view_resolver';
import {DirectiveResolver} from './directive_resolver';
import {PipeResolver} from './pipe_resolver';
function _createCompilerConfig() {
return new CompilerConfig(assertionsEnabled(), false, true);
}
/**
* A set of providers that provide `RuntimeCompiler` and its dependencies to use for
* template compilation.
*/
export const COMPILER_PROVIDERS: Array<any | Type | {[k: string]: any} | any[]> =
/*@ts2dart_const*/[
Lexer,
Parser,
HtmlParser,
TemplateParser,
DirectiveNormalizer,
CompileMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
ViewCompiler,
/*@ts2dart_Provider*/ {provide: CompilerConfig, useFactory: _createCompilerConfig, deps: []},
RuntimeCompiler,
/*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler},
DomElementSchemaRegistry,
/*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
UrlResolver,
ViewResolver,
DirectiveResolver,
PipeResolver
];

View File

@ -1,38 +0,0 @@
import {isBlank} from '../src/facade/lang';
import {unimplemented} from '../src/facade/exceptions';
import {Identifiers} from './identifiers';
import {CompileIdentifierMetadata} from './compile_metadata';
export class CompilerConfig {
public renderTypes: RenderTypes;
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
public useJit: boolean, renderTypes: RenderTypes = null) {
if (isBlank(renderTypes)) {
renderTypes = new DefaultRenderTypes();
}
this.renderTypes = renderTypes;
}
}
/**
* Types used for the renderer.
* Can be replaced to specialize the generated output to a specific renderer
* to help tree shaking.
*/
export abstract class RenderTypes {
get renderer(): CompileIdentifierMetadata { return unimplemented(); }
get renderText(): CompileIdentifierMetadata { return unimplemented(); }
get renderElement(): CompileIdentifierMetadata { return unimplemented(); }
get renderComment(): CompileIdentifierMetadata { return unimplemented(); }
get renderNode(): CompileIdentifierMetadata { return unimplemented(); }
get renderEvent(): CompileIdentifierMetadata { return unimplemented(); }
}
export class DefaultRenderTypes implements RenderTypes {
renderer = Identifiers.Renderer;
renderText = null;
renderElement = null;
renderComment = null;
renderNode = null;
renderEvent = null;
}

View File

@ -1,756 +0,0 @@
import {NumberWrapper, StringWrapper, isPresent, resolveEnumToken} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {
isWhitespace,
$EOF,
$HASH,
$TILDA,
$CARET,
$PERCENT,
$$,
$_,
$COLON,
$SQ,
$DQ,
$EQ,
$SLASH,
$BACKSLASH,
$PERIOD,
$STAR,
$PLUS,
$LPAREN,
$RPAREN,
$LBRACE,
$RBRACE,
$LBRACKET,
$RBRACKET,
$PIPE,
$COMMA,
$SEMICOLON,
$MINUS,
$BANG,
$QUESTION,
$AT,
$AMPERSAND,
$GT,
$a,
$A,
$z,
$Z,
$0,
$9,
$FF,
$CR,
$LF,
$VTAB
} from '@angular/compiler/src/chars';
export {
$EOF,
$AT,
$RBRACE,
$LBRACE,
$LBRACKET,
$RBRACKET,
$LPAREN,
$RPAREN,
$COMMA,
$COLON,
$SEMICOLON,
isWhitespace
} from '@angular/compiler/src/chars';
export enum CssTokenType {
EOF,
String,
Comment,
Identifier,
Number,
IdentifierOrNumber,
AtKeyword,
Character,
Whitespace,
Invalid
}
export enum CssLexerMode {
ALL,
ALL_TRACK_WS,
SELECTOR,
PSEUDO_SELECTOR,
ATTRIBUTE_SELECTOR,
AT_RULE_QUERY,
MEDIA_QUERY,
BLOCK,
KEYFRAME_BLOCK,
STYLE_BLOCK,
STYLE_VALUE,
STYLE_VALUE_FUNCTION,
STYLE_CALC_FUNCTION
}
export class LexedCssResult {
constructor(public error: CssScannerError, public token: CssToken) {}
}
export function generateErrorMessage(input: string, message: string, errorValue: string,
index: number, row: number, column: number): string {
return `${message} at column ${row}:${column} in expression [` +
findProblemCode(input, errorValue, index, column) + ']';
}
export function findProblemCode(input: string, errorValue: string, index: number,
column: number): string {
var endOfProblemLine = index;
var current = charCode(input, index);
while (current > 0 && !isNewline(current)) {
current = charCode(input, ++endOfProblemLine);
}
var choppedString = input.substring(0, endOfProblemLine);
var pointerPadding = "";
for (var i = 0; i < column; i++) {
pointerPadding += " ";
}
var pointerString = "";
for (var i = 0; i < errorValue.length; i++) {
pointerString += "^";
}
return choppedString + "\n" + pointerPadding + pointerString + "\n";
}
export class CssToken {
numValue: number;
constructor(public index: number, public column: number, public line: number,
public type: CssTokenType, public strValue: string) {
this.numValue = charCode(strValue, 0);
}
}
export class CssLexer {
scan(text: string, trackComments: boolean = false): CssScanner {
return new CssScanner(text, trackComments);
}
}
export class CssScannerError extends BaseException {
public rawMessage: string;
public message: string;
constructor(public token: CssToken, message) {
super('Css Parse Error: ' + message);
this.rawMessage = message;
}
toString(): string { return this.message; }
}
function _trackWhitespace(mode: CssLexerMode) {
switch (mode) {
case CssLexerMode.SELECTOR:
case CssLexerMode.ALL_TRACK_WS:
case CssLexerMode.STYLE_VALUE:
return true;
default:
return false;
}
}
export class CssScanner {
peek: number;
peekPeek: number;
length: number = 0;
index: number = -1;
column: number = -1;
line: number = 0;
/** @internal */
_currentMode: CssLexerMode = CssLexerMode.BLOCK;
/** @internal */
_currentError: CssScannerError = null;
constructor(public input: string, private _trackComments: boolean = false) {
this.length = this.input.length;
this.peekPeek = this.peekAt(0);
this.advance();
}
getMode(): CssLexerMode { return this._currentMode; }
setMode(mode: CssLexerMode) {
if (this._currentMode != mode) {
if (_trackWhitespace(this._currentMode)) {
this.consumeWhitespace();
}
this._currentMode = mode;
}
}
advance(): void {
if (isNewline(this.peek)) {
this.column = 0;
this.line++;
} else {
this.column++;
}
this.index++;
this.peek = this.peekPeek;
this.peekPeek = this.peekAt(this.index + 1);
}
peekAt(index: number): number {
return index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, index);
}
consumeEmptyStatements(): void {
this.consumeWhitespace();
while (this.peek == $SEMICOLON) {
this.advance();
this.consumeWhitespace();
}
}
consumeWhitespace(): void {
while (isWhitespace(this.peek) || isNewline(this.peek)) {
this.advance();
if (!this._trackComments && isCommentStart(this.peek, this.peekPeek)) {
this.advance(); // /
this.advance(); // *
while (!isCommentEnd(this.peek, this.peekPeek)) {
if (this.peek == $EOF) {
this.error('Unterminated comment');
}
this.advance();
}
this.advance(); // *
this.advance(); // /
}
}
}
consume(type: CssTokenType, value: string = null): LexedCssResult {
var mode = this._currentMode;
this.setMode(CssLexerMode.ALL);
var previousIndex = this.index;
var previousLine = this.line;
var previousColumn = this.column;
var output = this.scan();
// just incase the inner scan method returned an error
if (isPresent(output.error)) {
this.setMode(mode);
return output;
}
var next = output.token;
if (!isPresent(next)) {
next = new CssToken(0, 0, 0, CssTokenType.EOF, "end of file");
}
var isMatchingType;
if (type == CssTokenType.IdentifierOrNumber) {
// TODO (matsko): implement array traversal for lookup here
isMatchingType = next.type == CssTokenType.Number || next.type == CssTokenType.Identifier;
} else {
isMatchingType = next.type == type;
}
// before throwing the error we need to bring back the former
// mode so that the parser can recover...
this.setMode(mode);
var error = null;
if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
var errorMessage = resolveEnumToken(CssTokenType, next.type) + " does not match expected " +
resolveEnumToken(CssTokenType, type) + " value";
if (isPresent(value)) {
errorMessage += ' ("' + next.strValue + '" should match "' + value + '")';
}
error = new CssScannerError(
next, generateErrorMessage(this.input, errorMessage, next.strValue, previousIndex,
previousLine, previousColumn));
}
return new LexedCssResult(error, next);
}
scan(): LexedCssResult {
var trackWS = _trackWhitespace(this._currentMode);
if (this.index == 0 && !trackWS) { // first scan
this.consumeWhitespace();
}
var token = this._scan();
if (token == null) return null;
var error = this._currentError;
this._currentError = null;
if (!trackWS) {
this.consumeWhitespace();
}
return new LexedCssResult(error, token);
}
/** @internal */
_scan(): CssToken {
var peek = this.peek;
var peekPeek = this.peekPeek;
if (peek == $EOF) return null;
if (isCommentStart(peek, peekPeek)) {
// even if comments are not tracked we still lex the
// comment so we can move the pointer forward
var commentToken = this.scanComment();
if (this._trackComments) {
return commentToken;
}
}
if (_trackWhitespace(this._currentMode) && (isWhitespace(peek) || isNewline(peek))) {
return this.scanWhitespace();
}
peek = this.peek;
peekPeek = this.peekPeek;
if (peek == $EOF) return null;
if (isStringStart(peek, peekPeek)) {
return this.scanString();
}
// something like url(cool)
if (this._currentMode == CssLexerMode.STYLE_VALUE_FUNCTION) {
return this.scanCssValueFunction();
}
var isModifier = peek == $PLUS || peek == $MINUS;
var digitA = isModifier ? false : isDigit(peek);
var digitB = isDigit(peekPeek);
if (digitA || (isModifier && (peekPeek == $PERIOD || digitB)) || (peek == $PERIOD && digitB)) {
return this.scanNumber();
}
if (peek == $AT) {
return this.scanAtExpression();
}
if (isIdentifierStart(peek, peekPeek)) {
return this.scanIdentifier();
}
if (isValidCssCharacter(peek, this._currentMode)) {
return this.scanCharacter();
}
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`);
}
scanComment(): CssToken {
if (this.assertCondition(isCommentStart(this.peek, this.peekPeek),
"Expected comment start value")) {
return null;
}
var start = this.index;
var startingColumn = this.column;
var startingLine = this.line;
this.advance(); // /
this.advance(); // *
while (!isCommentEnd(this.peek, this.peekPeek)) {
if (this.peek == $EOF) {
this.error('Unterminated comment');
}
this.advance();
}
this.advance(); // *
this.advance(); // /
var str = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, startingLine, CssTokenType.Comment, str);
}
scanWhitespace(): CssToken {
var start = this.index;
var startingColumn = this.column;
var startingLine = this.line;
while (isWhitespace(this.peek) && this.peek != $EOF) {
this.advance();
}
var str = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, startingLine, CssTokenType.Whitespace, str);
}
scanString(): CssToken {
if (this.assertCondition(isStringStart(this.peek, this.peekPeek),
"Unexpected non-string starting value")) {
return null;
}
var target = this.peek;
var start = this.index;
var startingColumn = this.column;
var startingLine = this.line;
var previous = target;
this.advance();
while (!isCharMatch(target, previous, this.peek)) {
if (this.peek == $EOF || isNewline(this.peek)) {
this.error('Unterminated quote');
}
previous = this.peek;
this.advance();
}
if (this.assertCondition(this.peek == target, "Unterminated quote")) {
return null;
}
this.advance();
var str = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, startingLine, CssTokenType.String, str);
}
scanNumber(): CssToken {
var start = this.index;
var startingColumn = this.column;
if (this.peek == $PLUS || this.peek == $MINUS) {
this.advance();
}
var periodUsed = false;
while (isDigit(this.peek) || this.peek == $PERIOD) {
if (this.peek == $PERIOD) {
if (periodUsed) {
this.error('Unexpected use of a second period value');
}
periodUsed = true;
}
this.advance();
}
var strValue = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, this.line, CssTokenType.Number, strValue);
}
scanIdentifier(): CssToken {
if (this.assertCondition(isIdentifierStart(this.peek, this.peekPeek),
'Expected identifier starting value')) {
return null;
}
var start = this.index;
var startingColumn = this.column;
while (isIdentifierPart(this.peek)) {
this.advance();
}
var strValue = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
}
scanCssValueFunction(): CssToken {
var start = this.index;
var startingColumn = this.column;
while (this.peek != $EOF && this.peek != $RPAREN) {
this.advance();
}
var strValue = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
}
scanCharacter(): CssToken {
var start = this.index;
var startingColumn = this.column;
if (this.assertCondition(isValidCssCharacter(this.peek, this._currentMode),
charStr(this.peek) + ' is not a valid CSS character')) {
return null;
}
var c = this.input.substring(start, start + 1);
this.advance();
return new CssToken(start, startingColumn, this.line, CssTokenType.Character, c);
}
scanAtExpression(): CssToken {
if (this.assertCondition(this.peek == $AT, 'Expected @ value')) {
return null;
}
var start = this.index;
var startingColumn = this.column;
this.advance();
if (isIdentifierStart(this.peek, this.peekPeek)) {
var ident = this.scanIdentifier();
var strValue = '@' + ident.strValue;
return new CssToken(start, startingColumn, this.line, CssTokenType.AtKeyword, strValue);
} else {
return this.scanCharacter();
}
}
assertCondition(status: boolean, errorMessage: string): boolean {
if (!status) {
this.error(errorMessage);
return true;
}
return false;
}
error(message: string, errorTokenValue: string = null, doNotAdvance: boolean = false): CssToken {
var index: number = this.index;
var column: number = this.column;
var line: number = this.line;
errorTokenValue =
isPresent(errorTokenValue) ? errorTokenValue : StringWrapper.fromCharCode(this.peek);
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
var errorMessage =
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
if (!doNotAdvance) {
this.advance();
}
this._currentError = new CssScannerError(invalidToken, errorMessage);
return invalidToken;
}
}
function isAtKeyword(current: CssToken, next: CssToken): boolean {
return current.numValue == $AT && next.type == CssTokenType.Identifier;
}
function isCharMatch(target: number, previous: number, code: number): boolean {
return code == target && previous != $BACKSLASH;
}
function isDigit(code: number): boolean {
return $0 <= code && code <= $9;
}
function isCommentStart(code: number, next: number): boolean {
return code == $SLASH && next == $STAR;
}
function isCommentEnd(code: number, next: number): boolean {
return code == $STAR && next == $SLASH;
}
function isStringStart(code: number, next: number): boolean {
var target = code;
if (target == $BACKSLASH) {
target = next;
}
return target == $DQ || target == $SQ;
}
function isIdentifierStart(code: number, next: number): boolean {
var target = code;
if (target == $MINUS) {
target = next;
}
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
target == $MINUS || target == $_;
}
function isIdentifierPart(target: number): boolean {
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
target == $MINUS || target == $_ || isDigit(target);
}
function isValidPseudoSelectorCharacter(code: number): boolean {
switch (code) {
case $LPAREN:
case $RPAREN:
return true;
default:
return false;
}
}
function isValidKeyframeBlockCharacter(code: number): boolean {
return code == $PERCENT;
}
function isValidAttributeSelectorCharacter(code: number): boolean {
// value^*|$~=something
switch (code) {
case $$:
case $PIPE:
case $CARET:
case $TILDA:
case $STAR:
case $EQ:
return true;
default:
return false;
}
}
function isValidSelectorCharacter(code: number): boolean {
// selector [ key = value ]
// IDENT C IDENT C IDENT C
// #id, .class, *+~>
// tag:PSEUDO
switch (code) {
case $HASH:
case $PERIOD:
case $TILDA:
case $STAR:
case $PLUS:
case $GT:
case $COLON:
case $PIPE:
case $COMMA:
return true;
default:
return false;
}
}
function isValidStyleBlockCharacter(code: number): boolean {
// key:value;
// key:calc(something ... )
switch (code) {
case $HASH:
case $SEMICOLON:
case $COLON:
case $PERCENT:
case $SLASH:
case $BACKSLASH:
case $BANG:
case $PERIOD:
case $LPAREN:
case $RPAREN:
return true;
default:
return false;
}
}
function isValidMediaQueryRuleCharacter(code: number): boolean {
// (min-width: 7.5em) and (orientation: landscape)
switch (code) {
case $LPAREN:
case $RPAREN:
case $COLON:
case $PERCENT:
case $PERIOD:
return true;
default:
return false;
}
}
function isValidAtRuleCharacter(code: number): boolean {
// @document url(http://www.w3.org/page?something=on#hash),
switch (code) {
case $LPAREN:
case $RPAREN:
case $COLON:
case $PERCENT:
case $PERIOD:
case $SLASH:
case $BACKSLASH:
case $HASH:
case $EQ:
case $QUESTION:
case $AMPERSAND:
case $STAR:
case $COMMA:
case $MINUS:
case $PLUS:
return true;
default:
return false;
}
}
function isValidStyleFunctionCharacter(code: number): boolean {
switch (code) {
case $PERIOD:
case $MINUS:
case $PLUS:
case $STAR:
case $SLASH:
case $LPAREN:
case $RPAREN:
case $COMMA:
return true;
default:
return false;
}
}
function isValidBlockCharacter(code: number): boolean {
// @something { }
// IDENT
return code == $AT;
}
function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
switch (mode) {
case CssLexerMode.ALL:
case CssLexerMode.ALL_TRACK_WS:
return true;
case CssLexerMode.SELECTOR:
return isValidSelectorCharacter(code);
case CssLexerMode.PSEUDO_SELECTOR:
return isValidPseudoSelectorCharacter(code);
case CssLexerMode.ATTRIBUTE_SELECTOR:
return isValidAttributeSelectorCharacter(code);
case CssLexerMode.MEDIA_QUERY:
return isValidMediaQueryRuleCharacter(code);
case CssLexerMode.AT_RULE_QUERY:
return isValidAtRuleCharacter(code);
case CssLexerMode.KEYFRAME_BLOCK:
return isValidKeyframeBlockCharacter(code);
case CssLexerMode.STYLE_BLOCK:
case CssLexerMode.STYLE_VALUE:
return isValidStyleBlockCharacter(code);
case CssLexerMode.STYLE_CALC_FUNCTION:
return isValidStyleFunctionCharacter(code);
case CssLexerMode.BLOCK:
return isValidBlockCharacter(code);
default:
return false;
}
}
function charCode(input, index): number {
return index >= input.length ? $EOF : StringWrapper.charCodeAt(input, index);
}
function charStr(code: number): string {
return StringWrapper.fromCharCode(code);
}
export function isNewline(code): boolean {
switch (code) {
case $FF:
case $CR:
case $LF:
case $VTAB:
return true;
default:
return false;
}
}

View File

@ -1,740 +0,0 @@
import {
ParseSourceSpan,
ParseSourceFile,
ParseLocation,
ParseError
} from '@angular/compiler/src/parse_util';
import {
bitWiseOr,
bitWiseAnd,
NumberWrapper,
StringWrapper,
isPresent
} from '../../src/facade/lang';
import {
CssLexerMode,
CssToken,
CssTokenType,
CssScanner,
CssScannerError,
generateErrorMessage,
$AT,
$EOF,
$RBRACE,
$LBRACE,
$LBRACKET,
$RBRACKET,
$LPAREN,
$RPAREN,
$COMMA,
$COLON,
$SEMICOLON,
isNewline
} from '@angular/compiler/src/css/lexer';
export {CssToken} from '@angular/compiler/src/css/lexer';
export enum BlockType {
Import,
Charset,
Namespace,
Supports,
Keyframes,
MediaQuery,
Selector,
FontFace,
Page,
Document,
Viewport,
Unsupported
}
const EOF_DELIM = 1;
const RBRACE_DELIM = 2;
const LBRACE_DELIM = 4;
const COMMA_DELIM = 8;
const COLON_DELIM = 16;
const SEMICOLON_DELIM = 32;
const NEWLINE_DELIM = 64;
const RPAREN_DELIM = 128;
function mergeTokens(tokens: CssToken[], separator: string = ""): CssToken {
var mainToken = tokens[0];
var str = mainToken.strValue;
for (var i = 1; i < tokens.length; i++) {
str += separator + tokens[i].strValue;
}
return new CssToken(mainToken.index, mainToken.column, mainToken.line, mainToken.type, str);
}
function getDelimFromToken(token: CssToken): number {
return getDelimFromCharacter(token.numValue);
}
function getDelimFromCharacter(code: number): number {
switch (code) {
case $EOF:
return EOF_DELIM;
case $COMMA:
return COMMA_DELIM;
case $COLON:
return COLON_DELIM;
case $SEMICOLON:
return SEMICOLON_DELIM;
case $RBRACE:
return RBRACE_DELIM;
case $LBRACE:
return LBRACE_DELIM;
case $RPAREN:
return RPAREN_DELIM;
default:
return isNewline(code) ? NEWLINE_DELIM : 0;
}
}
function characterContainsDelimiter(code: number, delimiters: number): boolean {
return bitWiseAnd([getDelimFromCharacter(code), delimiters]) > 0;
}
export class CssAST {
visit(visitor: CssASTVisitor, context?: any): void {}
}
export interface CssASTVisitor {
visitCssValue(ast: CssStyleValueAST, context?: any): void;
visitInlineCssRule(ast: CssInlineRuleAST, context?: any): void;
visitCssKeyframeRule(ast: CssKeyframeRuleAST, context?: any): void;
visitCssKeyframeDefinition(ast: CssKeyframeDefinitionAST, context?: any): void;
visitCssMediaQueryRule(ast: CssMediaQueryRuleAST, context?: any): void;
visitCssSelectorRule(ast: CssSelectorRuleAST, context?: any): void;
visitCssSelector(ast: CssSelectorAST, context?: any): void;
visitCssDefinition(ast: CssDefinitionAST, context?: any): void;
visitCssBlock(ast: CssBlockAST, context?: any): void;
visitCssStyleSheet(ast: CssStyleSheetAST, context?: any): void;
visitUnkownRule(ast: CssUnknownTokenListAST, context?: any): void;
}
export class ParsedCssResult {
constructor(public errors: CssParseError[], public ast: CssStyleSheetAST) {}
}
export class CssParser {
private _errors: CssParseError[] = [];
private _file: ParseSourceFile;
constructor(private _scanner: CssScanner, private _fileName: string) {
this._file = new ParseSourceFile(this._scanner.input, _fileName);
}
/** @internal */
_resolveBlockType(token: CssToken): BlockType {
switch (token.strValue) {
case '@-o-keyframes':
case '@-moz-keyframes':
case '@-webkit-keyframes':
case '@keyframes':
return BlockType.Keyframes;
case '@charset':
return BlockType.Charset;
case '@import':
return BlockType.Import;
case '@namespace':
return BlockType.Namespace;
case '@page':
return BlockType.Page;
case '@document':
return BlockType.Document;
case '@media':
return BlockType.MediaQuery;
case '@font-face':
return BlockType.FontFace;
case '@viewport':
return BlockType.Viewport;
case '@supports':
return BlockType.Supports;
default:
return BlockType.Unsupported;
}
}
parse(): ParsedCssResult {
var delimiters: number = EOF_DELIM;
var ast = this._parseStyleSheet(delimiters);
var errors = this._errors;
this._errors = [];
return new ParsedCssResult(errors, ast);
}
/** @internal */
_parseStyleSheet(delimiters): CssStyleSheetAST {
var results = [];
this._scanner.consumeEmptyStatements();
while (this._scanner.peek != $EOF) {
this._scanner.setMode(CssLexerMode.BLOCK);
results.push(this._parseRule(delimiters));
}
return new CssStyleSheetAST(results);
}
/** @internal */
_parseRule(delimiters: number): CssRuleAST {
if (this._scanner.peek == $AT) {
return this._parseAtRule(delimiters);
}
return this._parseSelectorRule(delimiters);
}
/** @internal */
_parseAtRule(delimiters: number): CssRuleAST {
this._scanner.setMode(CssLexerMode.BLOCK);
var token = this._scan();
this._assertCondition(token.type == CssTokenType.AtKeyword,
`The CSS Rule ${token.strValue} is not a valid [@] rule.`, token);
var block, type = this._resolveBlockType(token);
switch (type) {
case BlockType.Charset:
case BlockType.Namespace:
case BlockType.Import:
var value = this._parseValue(delimiters);
this._scanner.setMode(CssLexerMode.BLOCK);
this._scanner.consumeEmptyStatements();
return new CssInlineRuleAST(type, value);
case BlockType.Viewport:
case BlockType.FontFace:
block = this._parseStyleBlock(delimiters);
return new CssBlockRuleAST(type, block);
case BlockType.Keyframes:
var tokens = this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]));
// keyframes only have one identifier name
var name = tokens[0];
return new CssKeyframeRuleAST(name, this._parseKeyframeBlock(delimiters));
case BlockType.MediaQuery:
this._scanner.setMode(CssLexerMode.MEDIA_QUERY);
var tokens = this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]));
return new CssMediaQueryRuleAST(tokens, this._parseBlock(delimiters));
case BlockType.Document:
case BlockType.Supports:
case BlockType.Page:
this._scanner.setMode(CssLexerMode.AT_RULE_QUERY);
var tokens = this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]));
return new CssBlockDefinitionRuleAST(type, tokens, this._parseBlock(delimiters));
// if a custom @rule { ... } is used it should still tokenize the insides
default:
var listOfTokens = [];
this._scanner.setMode(CssLexerMode.ALL);
this._error(generateErrorMessage(
this._scanner.input,
`The CSS "at" rule "${token.strValue}" is not allowed to used here`,
token.strValue, token.index, token.line, token.column),
token);
this._collectUntilDelim(bitWiseOr([delimiters, LBRACE_DELIM, SEMICOLON_DELIM]))
.forEach((token) => { listOfTokens.push(token); });
if (this._scanner.peek == $LBRACE) {
this._consume(CssTokenType.Character, '{');
this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]))
.forEach((token) => { listOfTokens.push(token); });
this._consume(CssTokenType.Character, '}');
}
return new CssUnknownTokenListAST(token, listOfTokens);
}
}
/** @internal */
_parseSelectorRule(delimiters: number): CssSelectorRuleAST {
var selectors = this._parseSelectors(delimiters);
var block = this._parseStyleBlock(delimiters);
this._scanner.setMode(CssLexerMode.BLOCK);
this._scanner.consumeEmptyStatements();
return new CssSelectorRuleAST(selectors, block);
}
/** @internal */
_parseSelectors(delimiters: number): CssSelectorAST[] {
delimiters = bitWiseOr([delimiters, LBRACE_DELIM]);
var selectors = [];
var isParsingSelectors = true;
while (isParsingSelectors) {
selectors.push(this._parseSelector(delimiters));
isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters);
if (isParsingSelectors) {
this._consume(CssTokenType.Character, ',');
isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters);
}
}
return selectors;
}
/** @internal */
_scan(): CssToken {
var output = this._scanner.scan();
var token = output.token;
var error = output.error;
if (isPresent(error)) {
this._error(error.rawMessage, token);
}
return token;
}
/** @internal */
_consume(type: CssTokenType, value: string = null): CssToken {
var output = this._scanner.consume(type, value);
var token = output.token;
var error = output.error;
if (isPresent(error)) {
this._error(error.rawMessage, token);
}
return token;
}
/** @internal */
_parseKeyframeBlock(delimiters: number): CssBlockAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
this._consume(CssTokenType.Character, '{');
var definitions = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
definitions.push(this._parseKeyframeDefinition(delimiters));
}
this._consume(CssTokenType.Character, '}');
return new CssBlockAST(definitions);
}
/** @internal */
_parseKeyframeDefinition(delimiters: number): CssKeyframeDefinitionAST {
var stepTokens = [];
delimiters = bitWiseOr([delimiters, LBRACE_DELIM]);
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
stepTokens.push(this._parseKeyframeLabel(bitWiseOr([delimiters, COMMA_DELIM])));
if (this._scanner.peek != $LBRACE) {
this._consume(CssTokenType.Character, ',');
}
}
var styles = this._parseStyleBlock(bitWiseOr([delimiters, RBRACE_DELIM]));
this._scanner.setMode(CssLexerMode.BLOCK);
return new CssKeyframeDefinitionAST(stepTokens, styles);
}
/** @internal */
_parseKeyframeLabel(delimiters: number): CssToken {
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
return mergeTokens(this._collectUntilDelim(delimiters));
}
/** @internal */
_parseSelector(delimiters: number): CssSelectorAST {
delimiters = bitWiseOr([delimiters, COMMA_DELIM, LBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.SELECTOR);
var selectorCssTokens = [];
var isComplex = false;
var wsCssToken;
var previousToken;
var parenCount = 0;
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
var code = this._scanner.peek;
switch (code) {
case $LPAREN:
parenCount++;
break;
case $RPAREN:
parenCount--;
break;
case $COLON:
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR);
previousToken = this._consume(CssTokenType.Character, ':');
selectorCssTokens.push(previousToken);
continue;
case $LBRACKET:
// if we are already inside an attribute selector then we can't
// jump into the mode again. Therefore this error will get picked
// up when the scan method is called below.
if (this._scanner.getMode() != CssLexerMode.ATTRIBUTE_SELECTOR) {
selectorCssTokens.push(this._consume(CssTokenType.Character, '['));
this._scanner.setMode(CssLexerMode.ATTRIBUTE_SELECTOR);
continue;
}
break;
case $RBRACKET:
selectorCssTokens.push(this._consume(CssTokenType.Character, ']'));
this._scanner.setMode(CssLexerMode.SELECTOR);
continue;
}
var token = this._scan();
// special case for the ":not(" selector since it
// contains an inner selector that needs to be parsed
// in isolation
if (this._scanner.getMode() == CssLexerMode.PSEUDO_SELECTOR && isPresent(previousToken) &&
previousToken.numValue == $COLON && token.strValue == "not" &&
this._scanner.peek == $LPAREN) {
selectorCssTokens.push(token);
selectorCssTokens.push(this._consume(CssTokenType.Character, '('));
// the inner selector inside of :not(...) can only be one
// CSS selector (no commas allowed) therefore we parse only
// one selector by calling the method below
this._parseSelector(bitWiseOr([delimiters, RPAREN_DELIM]))
.tokens.forEach(
(innerSelectorToken) => { selectorCssTokens.push(innerSelectorToken); });
selectorCssTokens.push(this._consume(CssTokenType.Character, ')'));
continue;
}
previousToken = token;
if (token.type == CssTokenType.Whitespace) {
wsCssToken = token;
} else {
if (isPresent(wsCssToken)) {
selectorCssTokens.push(wsCssToken);
wsCssToken = null;
isComplex = true;
}
selectorCssTokens.push(token);
}
}
if (this._scanner.getMode() == CssLexerMode.ATTRIBUTE_SELECTOR) {
this._error(
`Unbalanced CSS attribute selector at column ${previousToken.line}:${previousToken.column}`,
previousToken);
} else if (parenCount > 0) {
this._error(
`Unbalanced pseudo selector function value at column ${previousToken.line}:${previousToken.column}`,
previousToken);
}
return new CssSelectorAST(selectorCssTokens, isComplex);
}
/** @internal */
_parseValue(delimiters: number): CssStyleValueAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM, SEMICOLON_DELIM, NEWLINE_DELIM]);
this._scanner.setMode(CssLexerMode.STYLE_VALUE);
var strValue = "";
var tokens = [];
var previous: CssToken;
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
var token;
if (isPresent(previous) && previous.type == CssTokenType.Identifier &&
this._scanner.peek == $LPAREN) {
token = this._consume(CssTokenType.Character, '(');
tokens.push(token);
strValue += token.strValue;
this._scanner.setMode(CssLexerMode.STYLE_VALUE_FUNCTION);
token = this._scan();
tokens.push(token);
strValue += token.strValue;
this._scanner.setMode(CssLexerMode.STYLE_VALUE);
token = this._consume(CssTokenType.Character, ')');
tokens.push(token);
strValue += token.strValue;
} else {
token = this._scan();
if (token.type != CssTokenType.Whitespace) {
tokens.push(token);
}
strValue += token.strValue;
}
previous = token;
}
this._scanner.consumeWhitespace();
var code = this._scanner.peek;
if (code == $SEMICOLON) {
this._consume(CssTokenType.Character, ';');
} else if (code != $RBRACE) {
this._error(
generateErrorMessage(this._scanner.input,
`The CSS key/value definition did not end with a semicolon`,
previous.strValue, previous.index, previous.line, previous.column),
previous);
}
return new CssStyleValueAST(tokens, strValue);
}
/** @internal */
_collectUntilDelim(delimiters: number, assertType: CssTokenType = null): CssToken[] {
var tokens = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
var val = isPresent(assertType) ? this._consume(assertType) : this._scan();
tokens.push(val);
}
return tokens;
}
/** @internal */
_parseBlock(delimiters: number): CssBlockAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.BLOCK);
this._consume(CssTokenType.Character, '{');
this._scanner.consumeEmptyStatements();
var results = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
results.push(this._parseRule(delimiters));
}
this._consume(CssTokenType.Character, '}');
this._scanner.setMode(CssLexerMode.BLOCK);
this._scanner.consumeEmptyStatements();
return new CssBlockAST(results);
}
/** @internal */
_parseStyleBlock(delimiters: number): CssBlockAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
this._consume(CssTokenType.Character, '{');
this._scanner.consumeEmptyStatements();
var definitions = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
definitions.push(this._parseDefinition(delimiters));
this._scanner.consumeEmptyStatements();
}
this._consume(CssTokenType.Character, '}');
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
this._scanner.consumeEmptyStatements();
return new CssBlockAST(definitions);
}
/** @internal */
_parseDefinition(delimiters: number): CssDefinitionAST {
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
var prop = this._consume(CssTokenType.Identifier);
var parseValue, value = null;
// the colon value separates the prop from the style.
// there are a few cases as to what could happen if it
// is missing
switch (this._scanner.peek) {
case $COLON:
this._consume(CssTokenType.Character, ':');
parseValue = true;
break;
case $SEMICOLON:
case $RBRACE:
case $EOF:
parseValue = false;
break;
default:
var propStr = [prop.strValue];
if (this._scanner.peek != $COLON) {
// this will throw the error
var nextValue = this._consume(CssTokenType.Character, ':');
propStr.push(nextValue.strValue);
var remainingTokens = this._collectUntilDelim(
bitWiseOr([delimiters, COLON_DELIM, SEMICOLON_DELIM]), CssTokenType.Identifier);
if (remainingTokens.length > 0) {
remainingTokens.forEach((token) => { propStr.push(token.strValue); });
}
prop = new CssToken(prop.index, prop.column, prop.line, prop.type, propStr.join(" "));
}
// this means we've reached the end of the definition and/or block
if (this._scanner.peek == $COLON) {
this._consume(CssTokenType.Character, ':');
parseValue = true;
} else {
parseValue = false;
}
break;
}
if (parseValue) {
value = this._parseValue(delimiters);
} else {
this._error(generateErrorMessage(this._scanner.input,
`The CSS property was not paired with a style value`,
prop.strValue, prop.index, prop.line, prop.column),
prop);
}
return new CssDefinitionAST(prop, value);
}
/** @internal */
_assertCondition(status: boolean, errorMessage: string, problemToken: CssToken): boolean {
if (!status) {
this._error(errorMessage, problemToken);
return true;
}
return false;
}
/** @internal */
_error(message: string, problemToken: CssToken) {
var length = problemToken.strValue.length;
var error = CssParseError.create(this._file, 0, problemToken.line, problemToken.column, length,
message);
this._errors.push(error);
}
}
export class CssStyleValueAST extends CssAST {
constructor(public tokens: CssToken[], public strValue: string) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssValue(this); }
}
export class CssRuleAST extends CssAST {}
export class CssBlockRuleAST extends CssRuleAST {
constructor(public type: BlockType, public block: CssBlockAST, public name: CssToken = null) {
super();
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssBlock(this.block, context); }
}
export class CssKeyframeRuleAST extends CssBlockRuleAST {
constructor(name: CssToken, block: CssBlockAST) { super(BlockType.Keyframes, block, name); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssKeyframeRule(this, context); }
}
export class CssKeyframeDefinitionAST extends CssBlockRuleAST {
public steps;
constructor(_steps: CssToken[], block: CssBlockAST) {
super(BlockType.Keyframes, block, mergeTokens(_steps, ","));
this.steps = _steps;
}
visit(visitor: CssASTVisitor, context?: any) {
visitor.visitCssKeyframeDefinition(this, context);
}
}
export class CssBlockDefinitionRuleAST extends CssBlockRuleAST {
public strValue: string;
constructor(type: BlockType, public query: CssToken[], block: CssBlockAST) {
super(type, block);
this.strValue = query.map(token => token.strValue).join("");
var firstCssToken: CssToken = query[0];
this.name = new CssToken(firstCssToken.index, firstCssToken.column, firstCssToken.line,
CssTokenType.Identifier, this.strValue);
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssBlock(this.block, context); }
}
export class CssMediaQueryRuleAST extends CssBlockDefinitionRuleAST {
constructor(query: CssToken[], block: CssBlockAST) { super(BlockType.MediaQuery, query, block); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssMediaQueryRule(this, context); }
}
export class CssInlineRuleAST extends CssRuleAST {
constructor(public type: BlockType, public value: CssStyleValueAST) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitInlineCssRule(this, context); }
}
export class CssSelectorRuleAST extends CssBlockRuleAST {
public strValue: string;
constructor(public selectors: CssSelectorAST[], block: CssBlockAST) {
super(BlockType.Selector, block);
this.strValue = selectors.map(selector => selector.strValue).join(",");
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssSelectorRule(this, context); }
}
export class CssDefinitionAST extends CssAST {
constructor(public property: CssToken, public value: CssStyleValueAST) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssDefinition(this, context); }
}
export class CssSelectorAST extends CssAST {
public strValue;
constructor(public tokens: CssToken[], public isComplex: boolean = false) {
super();
this.strValue = tokens.map(token => token.strValue).join("");
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssSelector(this, context); }
}
export class CssBlockAST extends CssAST {
constructor(public entries: CssAST[]) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssBlock(this, context); }
}
export class CssStyleSheetAST extends CssAST {
constructor(public rules: CssAST[]) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssStyleSheet(this, context); }
}
export class CssParseError extends ParseError {
static create(file: ParseSourceFile, offset: number, line: number, col: number, length: number,
errMsg: string): CssParseError {
var start = new ParseLocation(file, offset, line, col);
var end = new ParseLocation(file, offset, line, col + length);
var span = new ParseSourceSpan(start, end);
return new CssParseError(span, "CSS Parse Error: " + errMsg);
}
constructor(span: ParseSourceSpan, message: string) { super(span, message); }
}
export class CssUnknownTokenListAST extends CssRuleAST {
constructor(public name, public tokens: CssToken[]) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitUnkownRule(this, context); }
}

View File

@ -1,348 +0,0 @@
import {ListWrapper} from '../../src/facade/collection';
export class AST {
visit(visitor: AstVisitor, context: any = null): any { return null; }
toString(): string { return "AST"; }
}
/**
* Represents a quoted expression of the form:
*
* quote = prefix `:` uninterpretedExpression
* prefix = identifier
* uninterpretedExpression = arbitrary string
*
* A quoted expression is meant to be pre-processed by an AST transformer that
* converts it into another AST that no longer contains quoted expressions.
* It is meant to allow third-party developers to extend Angular template
* expression language. The `uninterpretedExpression` part of the quote is
* therefore not interpreted by the Angular's own expression parser.
*/
export class Quote extends AST {
constructor(public prefix: string, public uninterpretedExpression: string, public location: any) {
super();
}
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitQuote(this, context); }
toString(): string { return "Quote"; }
}
export class EmptyExpr extends AST {
visit(visitor: AstVisitor, context: any = null) {
// do nothing
}
}
export class ImplicitReceiver extends AST {
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitImplicitReceiver(this, context);
}
}
/**
* Multiple expressions separated by a semicolon.
*/
export class Chain extends AST {
constructor(public expressions: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitChain(this, context); }
}
export class Conditional extends AST {
constructor(public condition: AST, public trueExp: AST, public falseExp: AST) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitConditional(this, context);
}
}
export class PropertyRead extends AST {
constructor(public receiver: AST, public name: string) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitPropertyRead(this, context);
}
}
export class PropertyWrite extends AST {
constructor(public receiver: AST, public name: string, public value: AST) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitPropertyWrite(this, context);
}
}
export class SafePropertyRead extends AST {
constructor(public receiver: AST, public name: string) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitSafePropertyRead(this, context);
}
}
export class KeyedRead extends AST {
constructor(public obj: AST, public key: AST) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitKeyedRead(this, context);
}
}
export class KeyedWrite extends AST {
constructor(public obj: AST, public key: AST, public value: AST) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitKeyedWrite(this, context);
}
}
export class BindingPipe extends AST {
constructor(public exp: AST, public name: string, public args: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitPipe(this, context); }
}
export class LiteralPrimitive extends AST {
constructor(public value) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitLiteralPrimitive(this, context);
}
}
export class LiteralArray extends AST {
constructor(public expressions: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitLiteralArray(this, context);
}
}
export class LiteralMap extends AST {
constructor(public keys: any[], public values: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitLiteralMap(this, context);
}
}
export class Interpolation extends AST {
constructor(public strings: any[], public expressions: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitInterpolation(this, context);
}
}
export class Binary extends AST {
constructor(public operation: string, public left: AST, public right: AST) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitBinary(this, context);
}
}
export class PrefixNot extends AST {
constructor(public expression: AST) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitPrefixNot(this, context);
}
}
export class MethodCall extends AST {
constructor(public receiver: AST, public name: string, public args: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitMethodCall(this, context);
}
}
export class SafeMethodCall extends AST {
constructor(public receiver: AST, public name: string, public args: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitSafeMethodCall(this, context);
}
}
export class FunctionCall extends AST {
constructor(public target: AST, public args: any[]) { super(); }
visit(visitor: AstVisitor, context: any = null): any {
return visitor.visitFunctionCall(this, context);
}
}
export class ASTWithSource extends AST {
constructor(public ast: AST, public source: string, public location: string) { super(); }
visit(visitor: AstVisitor, context: any = null): any { return this.ast.visit(visitor, context); }
toString(): string { return `${this.source} in ${this.location}`; }
}
export class TemplateBinding {
constructor(public key: string, public keyIsVar: boolean, public name: string,
public expression: ASTWithSource) {}
}
export interface AstVisitor {
visitBinary(ast: Binary, context: any): any;
visitChain(ast: Chain, context: any): any;
visitConditional(ast: Conditional, context: any): any;
visitFunctionCall(ast: FunctionCall, context: any): any;
visitImplicitReceiver(ast: ImplicitReceiver, context: any): any;
visitInterpolation(ast: Interpolation, context: any): any;
visitKeyedRead(ast: KeyedRead, context: any): any;
visitKeyedWrite(ast: KeyedWrite, context: any): any;
visitLiteralArray(ast: LiteralArray, context: any): any;
visitLiteralMap(ast: LiteralMap, context: any): any;
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any;
visitMethodCall(ast: MethodCall, context: any): any;
visitPipe(ast: BindingPipe, context: any): any;
visitPrefixNot(ast: PrefixNot, context: any): any;
visitPropertyRead(ast: PropertyRead, context: any): any;
visitPropertyWrite(ast: PropertyWrite, context: any): any;
visitQuote(ast: Quote, context: any): any;
visitSafeMethodCall(ast: SafeMethodCall, context: any): any;
visitSafePropertyRead(ast: SafePropertyRead, context: any): any;
}
export class RecursiveAstVisitor implements AstVisitor {
visitBinary(ast: Binary, context: any): any {
ast.left.visit(this);
ast.right.visit(this);
return null;
}
visitChain(ast: Chain, context: any): any { return this.visitAll(ast.expressions, context); }
visitConditional(ast: Conditional, context: any): any {
ast.condition.visit(this);
ast.trueExp.visit(this);
ast.falseExp.visit(this);
return null;
}
visitPipe(ast: BindingPipe, context: any): any {
ast.exp.visit(this);
this.visitAll(ast.args, context);
return null;
}
visitFunctionCall(ast: FunctionCall, context: any): any {
ast.target.visit(this);
this.visitAll(ast.args, context);
return null;
}
visitImplicitReceiver(ast: ImplicitReceiver, context: any): any { return null; }
visitInterpolation(ast: Interpolation, context: any): any {
return this.visitAll(ast.expressions, context);
}
visitKeyedRead(ast: KeyedRead, context: any): any {
ast.obj.visit(this);
ast.key.visit(this);
return null;
}
visitKeyedWrite(ast: KeyedWrite, context: any): any {
ast.obj.visit(this);
ast.key.visit(this);
ast.value.visit(this);
return null;
}
visitLiteralArray(ast: LiteralArray, context: any): any {
return this.visitAll(ast.expressions, context);
}
visitLiteralMap(ast: LiteralMap, context: any): any { return this.visitAll(ast.values, context); }
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any { return null; }
visitMethodCall(ast: MethodCall, context: any): any {
ast.receiver.visit(this);
return this.visitAll(ast.args, context);
}
visitPrefixNot(ast: PrefixNot, context: any): any {
ast.expression.visit(this);
return null;
}
visitPropertyRead(ast: PropertyRead, context: any): any {
ast.receiver.visit(this);
return null;
}
visitPropertyWrite(ast: PropertyWrite, context: any): any {
ast.receiver.visit(this);
ast.value.visit(this);
return null;
}
visitSafePropertyRead(ast: SafePropertyRead, context: any): any {
ast.receiver.visit(this);
return null;
}
visitSafeMethodCall(ast: SafeMethodCall, context: any): any {
ast.receiver.visit(this);
return this.visitAll(ast.args, context);
}
visitAll(asts: AST[], context: any): any {
asts.forEach(ast => ast.visit(this, context));
return null;
}
visitQuote(ast: Quote, context: any): any { return null; }
}
export class AstTransformer implements AstVisitor {
visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { return ast; }
visitInterpolation(ast: Interpolation, context: any): AST {
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
}
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): AST {
return new LiteralPrimitive(ast.value);
}
visitPropertyRead(ast: PropertyRead, context: any): AST {
return new PropertyRead(ast.receiver.visit(this), ast.name);
}
visitPropertyWrite(ast: PropertyWrite, context: any): AST {
return new PropertyWrite(ast.receiver.visit(this), ast.name, ast.value);
}
visitSafePropertyRead(ast: SafePropertyRead, context: any): AST {
return new SafePropertyRead(ast.receiver.visit(this), ast.name);
}
visitMethodCall(ast: MethodCall, context: any): AST {
return new MethodCall(ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
}
visitSafeMethodCall(ast: SafeMethodCall, context: any): AST {
return new SafeMethodCall(ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
}
visitFunctionCall(ast: FunctionCall, context: any): AST {
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
}
visitLiteralArray(ast: LiteralArray, context: any): AST {
return new LiteralArray(this.visitAll(ast.expressions));
}
visitLiteralMap(ast: LiteralMap, context: any): AST {
return new LiteralMap(ast.keys, this.visitAll(ast.values));
}
visitBinary(ast: Binary, context: any): AST {
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
}
visitPrefixNot(ast: PrefixNot, context: any): AST {
return new PrefixNot(ast.expression.visit(this));
}
visitConditional(ast: Conditional, context: any): AST {
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
ast.falseExp.visit(this));
}
visitPipe(ast: BindingPipe, context: any): AST {
return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args));
}
visitKeyedRead(ast: KeyedRead, context: any): AST {
return new KeyedRead(ast.obj.visit(this), ast.key.visit(this));
}
visitKeyedWrite(ast: KeyedWrite, context: any): AST {
return new KeyedWrite(ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
}
visitAll(asts: any[]): any[] {
var res = ListWrapper.createFixedSize(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
return res;
}
visitChain(ast: Chain, context: any): AST { return new Chain(this.visitAll(ast.expressions)); }
visitQuote(ast: Quote, context: any): AST {
return new Quote(ast.prefix, ast.uninterpretedExpression, ast.location);
}
}

View File

@ -1 +0,0 @@
../../facade/src

View File

@ -1,68 +0,0 @@
import {isPresent} from '../src/facade/lang';
import {ParseSourceSpan} from './parse_util';
export interface HtmlAst {
sourceSpan: ParseSourceSpan;
visit(visitor: HtmlAstVisitor, context: any): any;
}
export class HtmlTextAst implements HtmlAst {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitText(this, context); }
}
export class HtmlExpansionAst implements HtmlAst {
constructor(public switchValue: string, public type: string, public cases: HtmlExpansionCaseAst[],
public sourceSpan: ParseSourceSpan, public switchValueSourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any {
return visitor.visitExpansion(this, context);
}
}
export class HtmlExpansionCaseAst implements HtmlAst {
constructor(public value: string, public expression: HtmlAst[],
public sourceSpan: ParseSourceSpan, public valueSourceSpan: ParseSourceSpan,
public expSourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any {
return visitor.visitExpansionCase(this, context);
}
}
export class HtmlAttrAst implements HtmlAst {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitAttr(this, context); }
}
export class HtmlElementAst implements HtmlAst {
constructor(public name: string, public attrs: HtmlAttrAst[], public children: HtmlAst[],
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan,
public endSourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); }
}
export class HtmlCommentAst implements HtmlAst {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitComment(this, context); }
}
export interface HtmlAstVisitor {
visitElement(ast: HtmlElementAst, context: any): any;
visitAttr(ast: HtmlAttrAst, context: any): any;
visitText(ast: HtmlTextAst, context: any): any;
visitComment(ast: HtmlCommentAst, context: any): any;
visitExpansion(ast: HtmlExpansionAst, context: any): any;
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any;
}
export function htmlVisitAll(visitor: HtmlAstVisitor, asts: HtmlAst[], context: any = null): any[] {
var result = [];
asts.forEach(ast => {
var astResult = ast.visit(visitor, context);
if (isPresent(astResult)) {
result.push(astResult);
}
});
return result;
}

View File

@ -1,116 +0,0 @@
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
HtmlExpansionAst,
HtmlExpansionCaseAst,
htmlVisitAll
} from '../html_ast';
import {BaseException} from '../../src/facade/exceptions';
/**
* Expands special forms into elements.
*
* For example,
*
* ```
* { messages.length, plural,
* =0 {zero}
* =1 {one}
* =other {more than one}
* }
* ```
*
* will be expanded into
*
* ```
* <ul [ngPlural]="messages.length">
* <template [ngPluralCase]="0"><li i18n="plural_0">zero</li></template>
* <template [ngPluralCase]="1"><li i18n="plural_1">one</li></template>
* <template [ngPluralCase]="other"><li i18n="plural_other">more than one</li></template>
* </ul>
* ```
*/
export function expandNodes(nodes: HtmlAst[]): ExpansionResult {
let e = new _Expander();
let n = htmlVisitAll(e, nodes);
return new ExpansionResult(n, e.expanded);
}
export class ExpansionResult {
constructor(public nodes: HtmlAst[], public expanded: boolean) {}
}
class _Expander implements HtmlAstVisitor {
expanded: boolean = false;
constructor() {}
visitElement(ast: HtmlElementAst, context: any): any {
return new HtmlElementAst(ast.name, ast.attrs, htmlVisitAll(this, ast.children), ast.sourceSpan,
ast.startSourceSpan, ast.endSourceSpan);
}
visitAttr(ast: HtmlAttrAst, context: any): any { return ast; }
visitText(ast: HtmlTextAst, context: any): any { return ast; }
visitComment(ast: HtmlCommentAst, context: any): any { return ast; }
visitExpansion(ast: HtmlExpansionAst, context: any): any {
this.expanded = true;
return ast.type == "plural" ? _expandPluralForm(ast) : _expandDefaultForm(ast);
}
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any {
throw new BaseException("Should not be reached");
}
}
function _expandPluralForm(ast: HtmlExpansionAst): HtmlElementAst {
let children = ast.cases.map(c => {
let expansionResult = expandNodes(c.expression);
let i18nAttrs = expansionResult.expanded ?
[] :
[new HtmlAttrAst("i18n", `${ast.type}_${c.value}`, c.valueSourceSpan)];
return new HtmlElementAst(`template`,
[
new HtmlAttrAst("ngPluralCase", c.value, c.valueSourceSpan),
],
[
new HtmlElementAst(`li`, i18nAttrs, expansionResult.nodes,
c.sourceSpan, c.sourceSpan, c.sourceSpan)
],
c.sourceSpan, c.sourceSpan, c.sourceSpan);
});
let switchAttr = new HtmlAttrAst("[ngPlural]", ast.switchValue, ast.switchValueSourceSpan);
return new HtmlElementAst("ul", [switchAttr], children, ast.sourceSpan, ast.sourceSpan,
ast.sourceSpan);
}
function _expandDefaultForm(ast: HtmlExpansionAst): HtmlElementAst {
let children = ast.cases.map(c => {
let expansionResult = expandNodes(c.expression);
let i18nAttrs = expansionResult.expanded ?
[] :
[new HtmlAttrAst("i18n", `${ast.type}_${c.value}`, c.valueSourceSpan)];
return new HtmlElementAst(`template`,
[
new HtmlAttrAst("ngSwitchWhen", c.value, c.valueSourceSpan),
],
[
new HtmlElementAst(`li`, i18nAttrs, expansionResult.nodes,
c.sourceSpan, c.sourceSpan, c.sourceSpan)
],
c.sourceSpan, c.sourceSpan, c.sourceSpan);
});
let switchAttr = new HtmlAttrAst("[ngSwitch]", ast.switchValue, ast.switchValueSourceSpan);
return new HtmlElementAst("ul", [switchAttr], children, ast.sourceSpan, ast.sourceSpan,
ast.sourceSpan);
}

View File

@ -1,377 +0,0 @@
import {HtmlParser, HtmlParseTreeResult} from '../html_parser';
import {ParseSourceSpan, ParseError} from '../parse_util';
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
HtmlExpansionAst,
HtmlExpansionCaseAst,
htmlVisitAll
} from '../html_ast';
import {ListWrapper, StringMapWrapper} from '../../src/facade/collection';
import {RegExpWrapper, NumberWrapper, isPresent} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {Parser} from '../expression_parser/parser';
import {id} from './message';
import {expandNodes} from './expander';
import {
messageFromAttribute,
I18nError,
I18N_ATTR_PREFIX,
I18N_ATTR,
partition,
Part,
stringifyNodes,
meaning,
getPhNameFromBinding,
dedupePhName
} from './shared';
const _I18N_ATTR = "i18n";
const _PLACEHOLDER_ELEMENT = "ph";
const _NAME_ATTR = "name";
const _I18N_ATTR_PREFIX = "i18n-";
let _PLACEHOLDER_EXPANDED_REGEXP = RegExpWrapper.create(`\\<ph(\\s)+name=("(\\w)+")\\>\\<\\/ph\\>`);
/**
* Creates an i18n-ed version of the parsed template.
*
* Algorithm:
*
* To understand the algorithm, you need to know how partitioning works.
* Partitioning is required as we can use two i18n comments to group node siblings together.
* That is why we cannot just use nodes.
*
* Partitioning transforms an array of HtmlAst into an array of Part.
* A part can optionally contain a root element or a root text node. And it can also contain
* children.
* A part can contain i18n property, in which case it needs to be transalted.
*
* Example:
*
* The following array of nodes will be split into four parts:
*
* ```
* <a>A</a>
* <b i18n>B</b>
* <!-- i18n -->
* <c>C</c>
* D
* <!-- /i18n -->
* E
* ```
*
* Part 1 containing the a tag. It should not be translated.
* Part 2 containing the b tag. It should be translated.
* Part 3 containing the c tag and the D text node. It should be translated.
* Part 4 containing the E text node. It should not be translated.
*
*
* It is also important to understand how we stringify nodes to create a message.
*
* We walk the tree and replace every element node with a placeholder. We also replace
* all expressions in interpolation with placeholders. We also insert a placeholder element
* to wrap a text node containing interpolation.
*
* Example:
*
* The following tree:
*
* ```
* <a>A{{I}}</a><b>B</b>
* ```
*
* will be stringified into:
* ```
* <ph name="e0"><ph name="t1">A<ph name="0"/></ph></ph><ph name="e2">B</ph>
* ```
*
* This is what the algorithm does:
*
* 1. Use the provided html parser to get the html AST of the template.
* 2. Partition the root nodes, and process each part separately.
* 3. If a part does not have the i18n attribute, recurse to process children and attributes.
* 4. If a part has the i18n attribute, merge the translated i18n part with the original tree.
*
* This is how the merging works:
*
* 1. Use the stringify function to get the message id. Look up the message in the map.
* 2. Get the translated message. At this point we have two trees: the original tree
* and the translated tree, where all the elements are replaced with placeholders.
* 3. Use the original tree to create a mapping Index:number -> HtmlAst.
* 4. Walk the translated tree.
* 5. If we encounter a placeholder element, get is name property.
* 6. Get the type and the index of the node using the name property.
* 7. If the type is 'e', which means element, then:
* - translate the attributes of the original element
* - recurse to merge the children
* - create a new element using the original element name, original position,
* and translated children and attributes
* 8. If the type if 't', which means text, then:
* - get the list of expressions from the original node.
* - get the string version of the interpolation subtree
* - find all the placeholders in the translated message, and replace them with the
* corresponding original expressions
*/
export class I18nHtmlParser implements HtmlParser {
errors: ParseError[];
constructor(private _htmlParser: HtmlParser, private _parser: Parser,
private _messagesContent: string, private _messages: {[key: string]: HtmlAst[]}) {}
parse(sourceContent: string, sourceUrl: string,
parseExpansionForms: boolean = false): HtmlParseTreeResult {
this.errors = [];
let res = this._htmlParser.parse(sourceContent, sourceUrl, true);
if (res.errors.length > 0) {
return res;
} else {
let nodes = this._recurse(expandNodes(res.rootNodes).nodes);
return this.errors.length > 0 ? new HtmlParseTreeResult([], this.errors) :
new HtmlParseTreeResult(nodes, []);
}
}
private _processI18nPart(p: Part): HtmlAst[] {
try {
return p.hasI18n ? this._mergeI18Part(p) : this._recurseIntoI18nPart(p);
} catch (e) {
if (e instanceof I18nError) {
this.errors.push(e);
return [];
} else {
throw e;
}
}
}
private _mergeI18Part(p: Part): HtmlAst[] {
let message = p.createMessage(this._parser);
let messageId = id(message);
if (!StringMapWrapper.contains(this._messages, messageId)) {
throw new I18nError(
p.sourceSpan, `Cannot find message for id '${messageId}', content '${message.content}'.`);
}
let parsedMessage = this._messages[messageId];
return this._mergeTrees(p, parsedMessage, p.children);
}
private _recurseIntoI18nPart(p: Part): HtmlAst[] {
// we found an element without an i18n attribute
// we need to recurse in cause its children may have i18n set
// we also need to translate its attributes
if (isPresent(p.rootElement)) {
let root = p.rootElement;
let children = this._recurse(p.children);
let attrs = this._i18nAttributes(root);
return [
new HtmlElementAst(root.name, attrs, children, root.sourceSpan, root.startSourceSpan,
root.endSourceSpan)
];
// a text node without i18n or interpolation, nothing to do
} else if (isPresent(p.rootTextNode)) {
return [p.rootTextNode];
} else {
return this._recurse(p.children);
}
}
private _recurse(nodes: HtmlAst[]): HtmlAst[] {
let ps = partition(nodes, this.errors);
return ListWrapper.flatten(ps.map(p => this._processI18nPart(p)));
}
private _mergeTrees(p: Part, translated: HtmlAst[], original: HtmlAst[]): HtmlAst[] {
let l = new _CreateNodeMapping();
htmlVisitAll(l, original);
// merge the translated tree with the original tree.
// we do it by preserving the source code position of the original tree
let merged = this._mergeTreesHelper(translated, l.mapping);
// if the root element is present, we need to create a new root element with its attributes
// translated
if (isPresent(p.rootElement)) {
let root = p.rootElement;
let attrs = this._i18nAttributes(root);
return [
new HtmlElementAst(root.name, attrs, merged, root.sourceSpan, root.startSourceSpan,
root.endSourceSpan)
];
// this should never happen with a part. Parts that have root text node should not be merged.
} else if (isPresent(p.rootTextNode)) {
throw new BaseException("should not be reached");
} else {
return merged;
}
}
private _mergeTreesHelper(translated: HtmlAst[], mapping: HtmlAst[]): HtmlAst[] {
return translated.map(t => {
if (t instanceof HtmlElementAst) {
return this._mergeElementOrInterpolation(t, translated, mapping);
} else if (t instanceof HtmlTextAst) {
return t;
} else {
throw new BaseException("should not be reached");
}
});
}
private _mergeElementOrInterpolation(t: HtmlElementAst, translated: HtmlAst[],
mapping: HtmlAst[]): HtmlAst {
let name = this._getName(t);
let type = name[0];
let index = NumberWrapper.parseInt(name.substring(1), 10);
let originalNode = mapping[index];
if (type == "t") {
return this._mergeTextInterpolation(t, <HtmlTextAst>originalNode);
} else if (type == "e") {
return this._mergeElement(t, <HtmlElementAst>originalNode, mapping);
} else {
throw new BaseException("should not be reached");
}
}
private _getName(t: HtmlElementAst): string {
if (t.name != _PLACEHOLDER_ELEMENT) {
throw new I18nError(
t.sourceSpan,
`Unexpected tag "${t.name}". Only "${_PLACEHOLDER_ELEMENT}" tags are allowed.`);
}
let names = t.attrs.filter(a => a.name == _NAME_ATTR);
if (names.length == 0) {
throw new I18nError(t.sourceSpan, `Missing "${_NAME_ATTR}" attribute.`);
}
return names[0].value;
}
private _mergeTextInterpolation(t: HtmlElementAst, originalNode: HtmlTextAst): HtmlTextAst {
let split =
this._parser.splitInterpolation(originalNode.value, originalNode.sourceSpan.toString());
let exps = isPresent(split) ? split.expressions : [];
let messageSubstring =
this._messagesContent.substring(t.startSourceSpan.end.offset, t.endSourceSpan.start.offset);
let translated =
this._replacePlaceholdersWithExpressions(messageSubstring, exps, originalNode.sourceSpan);
return new HtmlTextAst(translated, originalNode.sourceSpan);
}
private _mergeElement(t: HtmlElementAst, originalNode: HtmlElementAst,
mapping: HtmlAst[]): HtmlElementAst {
let children = this._mergeTreesHelper(t.children, mapping);
return new HtmlElementAst(originalNode.name, this._i18nAttributes(originalNode), children,
originalNode.sourceSpan, originalNode.startSourceSpan,
originalNode.endSourceSpan);
}
private _i18nAttributes(el: HtmlElementAst): HtmlAttrAst[] {
let res = [];
el.attrs.forEach(attr => {
if (attr.name.startsWith(I18N_ATTR_PREFIX) || attr.name == I18N_ATTR) return;
let i18ns = el.attrs.filter(a => a.name == `i18n-${attr.name}`);
if (i18ns.length == 0) {
res.push(attr);
return;
}
let i18n = i18ns[0];
let message = messageFromAttribute(this._parser, el, i18n);
let messageId = id(message);
if (StringMapWrapper.contains(this._messages, messageId)) {
let updatedMessage = this._replaceInterpolationInAttr(attr, this._messages[messageId]);
res.push(new HtmlAttrAst(attr.name, updatedMessage, attr.sourceSpan));
} else {
throw new I18nError(
attr.sourceSpan,
`Cannot find message for id '${messageId}', content '${message.content}'.`);
}
});
return res;
}
private _replaceInterpolationInAttr(attr: HtmlAttrAst, msg: HtmlAst[]): string {
let split = this._parser.splitInterpolation(attr.value, attr.sourceSpan.toString());
let exps = isPresent(split) ? split.expressions : [];
let first = msg[0];
let last = msg[msg.length - 1];
let start = first.sourceSpan.start.offset;
let end =
last instanceof HtmlElementAst ? last.endSourceSpan.end.offset : last.sourceSpan.end.offset;
let messageSubstring = this._messagesContent.substring(start, end);
return this._replacePlaceholdersWithExpressions(messageSubstring, exps, attr.sourceSpan);
};
private _replacePlaceholdersWithExpressions(message: string, exps: string[],
sourceSpan: ParseSourceSpan): string {
let expMap = this._buildExprMap(exps);
return RegExpWrapper.replaceAll(_PLACEHOLDER_EXPANDED_REGEXP, message, (match) => {
let nameWithQuotes = match[2];
let name = nameWithQuotes.substring(1, nameWithQuotes.length - 1);
return this._convertIntoExpression(name, expMap, sourceSpan);
});
}
private _buildExprMap(exps: string[]): Map<string, string> {
let expMap = new Map<string, string>();
let usedNames = new Map<string, number>();
for (var i = 0; i < exps.length; i++) {
let phName = getPhNameFromBinding(exps[i], i);
expMap.set(dedupePhName(usedNames, phName), exps[i]);
}
return expMap;
}
private _convertIntoExpression(name: string, expMap: Map<string, string>,
sourceSpan: ParseSourceSpan) {
if (expMap.has(name)) {
return `{{${expMap.get(name)}}}`;
} else {
throw new I18nError(sourceSpan, `Invalid interpolation name '${name}'`);
}
}
}
class _CreateNodeMapping implements HtmlAstVisitor {
mapping: HtmlAst[] = [];
visitElement(ast: HtmlElementAst, context: any): any {
this.mapping.push(ast);
htmlVisitAll(this, ast.children);
return null;
}
visitAttr(ast: HtmlAttrAst, context: any): any { return null; }
visitText(ast: HtmlTextAst, context: any): any {
this.mapping.push(ast);
return null;
}
visitExpansion(ast: HtmlExpansionAst, context: any): any { return null; }
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
visitComment(ast: HtmlCommentAst, context: any): any { return ""; }
}

View File

@ -1,21 +0,0 @@
import {isPresent, escape} from '../../src/facade/lang';
/**
* A message extracted from a template.
*
* The identity of a message is comprised of `content` and `meaning`.
*
* `description` is additional information provided to the translator.
*/
export class Message {
constructor(public content: string, public meaning: string, public description: string = null) {}
}
/**
* Computes the id of a message
*/
export function id(m: Message): string {
let meaning = isPresent(m.meaning) ? m.meaning : "";
let content = isPresent(m.content) ? m.content : "";
return escape(`$ng|${meaning}|${content}`);
}

View File

@ -1,178 +0,0 @@
import {HtmlParser} from '../html_parser';
import {ParseSourceSpan, ParseError} from '../parse_util';
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
htmlVisitAll
} from '../html_ast';
import {isPresent} from '../../src/facade/lang';
import {StringMapWrapper} from '../../src/facade/collection';
import {Parser} from '../expression_parser/parser';
import {Message, id} from './message';
import {expandNodes} from './expander';
import {
I18nError,
Part,
I18N_ATTR_PREFIX,
partition,
meaning,
description,
stringifyNodes,
messageFromAttribute
} from './shared';
/**
* All messages extracted from a template.
*/
export class ExtractionResult {
constructor(public messages: Message[], public errors: ParseError[]) {}
}
/**
* Removes duplicate messages.
*
* E.g.
*
* ```
* var m = [new Message("message", "meaning", "desc1"), new Message("message", "meaning",
* "desc2")];
* expect(removeDuplicates(m)).toEqual([new Message("message", "meaning", "desc1")]);
* ```
*/
export function removeDuplicates(messages: Message[]): Message[] {
let uniq: {[key: string]: Message} = {};
messages.forEach(m => {
if (!StringMapWrapper.contains(uniq, id(m))) {
uniq[id(m)] = m;
}
});
return StringMapWrapper.values(uniq);
}
/**
* Extracts all messages from a template.
*
* Algorithm:
*
* To understand the algorithm, you need to know how partitioning works.
* Partitioning is required as we can use two i18n comments to group node siblings together.
* That is why we cannot just use nodes.
*
* Partitioning transforms an array of HtmlAst into an array of Part.
* A part can optionally contain a root element or a root text node. And it can also contain
* children.
* A part can contain i18n property, in which case it needs to be extracted.
*
* Example:
*
* The following array of nodes will be split into four parts:
*
* ```
* <a>A</a>
* <b i18n>B</b>
* <!-- i18n -->
* <c>C</c>
* D
* <!-- /i18n -->
* E
* ```
*
* Part 1 containing the a tag. It should not be translated.
* Part 2 containing the b tag. It should be translated.
* Part 3 containing the c tag and the D text node. It should be translated.
* Part 4 containing the E text node. It should not be translated..
*
* It is also important to understand how we stringify nodes to create a message.
*
* We walk the tree and replace every element node with a placeholder. We also replace
* all expressions in interpolation with placeholders. We also insert a placeholder element
* to wrap a text node containing interpolation.
*
* Example:
*
* The following tree:
*
* ```
* <a>A{{I}}</a><b>B</b>
* ```
*
* will be stringified into:
* ```
* <ph name="e0"><ph name="t1">A<ph name="0"/></ph></ph><ph name="e2">B</ph>
* ```
*
* This is what the algorithm does:
*
* 1. Use the provided html parser to get the html AST of the template.
* 2. Partition the root nodes, and process each part separately.
* 3. If a part does not have the i18n attribute, recurse to process children and attributes.
* 4. If a part has the i18n attribute, stringify the nodes to create a Message.
*/
export class MessageExtractor {
messages: Message[];
errors: ParseError[];
constructor(private _htmlParser: HtmlParser, private _parser: Parser) {}
extract(template: string, sourceUrl: string): ExtractionResult {
this.messages = [];
this.errors = [];
let res = this._htmlParser.parse(template, sourceUrl, true);
if (res.errors.length > 0) {
return new ExtractionResult([], res.errors);
} else {
this._recurse(expandNodes(res.rootNodes).nodes);
return new ExtractionResult(this.messages, this.errors);
}
}
private _extractMessagesFromPart(p: Part): void {
if (p.hasI18n) {
this.messages.push(p.createMessage(this._parser));
this._recurseToExtractMessagesFromAttributes(p.children);
} else {
this._recurse(p.children);
}
if (isPresent(p.rootElement)) {
this._extractMessagesFromAttributes(p.rootElement);
}
}
private _recurse(nodes: HtmlAst[]): void {
if (isPresent(nodes)) {
let ps = partition(nodes, this.errors);
ps.forEach(p => this._extractMessagesFromPart(p));
}
}
private _recurseToExtractMessagesFromAttributes(nodes: HtmlAst[]): void {
nodes.forEach(n => {
if (n instanceof HtmlElementAst) {
this._extractMessagesFromAttributes(n);
this._recurseToExtractMessagesFromAttributes(n.children);
}
});
}
private _extractMessagesFromAttributes(p: HtmlElementAst): void {
p.attrs.forEach(attr => {
if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
try {
this.messages.push(messageFromAttribute(this._parser, p, attr));
} catch (e) {
if (e instanceof I18nError) {
this.errors.push(e);
} else {
throw e;
}
}
}
});
}
}

View File

@ -1,191 +0,0 @@
import {ParseSourceSpan, ParseError} from '../parse_util';
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
HtmlExpansionAst,
HtmlExpansionCaseAst,
htmlVisitAll
} from '../html_ast';
import {isPresent, isBlank, StringWrapper} from '../../src/facade/lang';
import {Message} from './message';
import {Parser} from '../expression_parser/parser';
export const I18N_ATTR = "i18n";
export const I18N_ATTR_PREFIX = "i18n-";
var CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*"([\s\S]*?)"[\s\S]*\)/g;
/**
* An i18n error.
*/
export class I18nError extends ParseError {
constructor(span: ParseSourceSpan, msg: string) { super(span, msg); }
}
// Man, this is so ugly!
export function partition(nodes: HtmlAst[], errors: ParseError[]): Part[] {
let res = [];
for (let i = 0; i < nodes.length; ++i) {
let n = nodes[i];
let temp = [];
if (_isOpeningComment(n)) {
let i18n = (<HtmlCommentAst>n).value.substring(5).trim();
i++;
while (!_isClosingComment(nodes[i])) {
temp.push(nodes[i++]);
if (i === nodes.length) {
errors.push(new I18nError(n.sourceSpan, "Missing closing 'i18n' comment."));
break;
}
}
res.push(new Part(null, null, temp, i18n, true));
} else if (n instanceof HtmlElementAst) {
let i18n = _findI18nAttr(n);
res.push(new Part(n, null, n.children, isPresent(i18n) ? i18n.value : null, isPresent(i18n)));
} else if (n instanceof HtmlTextAst) {
res.push(new Part(null, n, null, null, false));
}
}
return res;
}
export class Part {
constructor(public rootElement: HtmlElementAst, public rootTextNode: HtmlTextAst,
public children: HtmlAst[], public i18n: string, public hasI18n: boolean) {}
get sourceSpan(): ParseSourceSpan {
if (isPresent(this.rootElement))
return this.rootElement.sourceSpan;
else if (isPresent(this.rootTextNode))
return this.rootTextNode.sourceSpan;
else
return this.children[0].sourceSpan;
}
createMessage(parser: Parser): Message {
return new Message(stringifyNodes(this.children, parser), meaning(this.i18n),
description(this.i18n));
}
}
function _isOpeningComment(n: HtmlAst): boolean {
return n instanceof HtmlCommentAst && isPresent(n.value) && n.value.startsWith("i18n:");
}
function _isClosingComment(n: HtmlAst): boolean {
return n instanceof HtmlCommentAst && isPresent(n.value) && n.value == "/i18n";
}
function _findI18nAttr(p: HtmlElementAst): HtmlAttrAst {
let i18n = p.attrs.filter(a => a.name == I18N_ATTR);
return i18n.length == 0 ? null : i18n[0];
}
export function meaning(i18n: string): string {
if (isBlank(i18n) || i18n == "") return null;
return i18n.split("|")[0];
}
export function description(i18n: string): string {
if (isBlank(i18n) || i18n == "") return null;
let parts = i18n.split("|");
return parts.length > 1 ? parts[1] : null;
}
export function messageFromAttribute(parser: Parser, p: HtmlElementAst,
attr: HtmlAttrAst): Message {
let expectedName = attr.name.substring(5);
let matching = p.attrs.filter(a => a.name == expectedName);
if (matching.length > 0) {
let value = removeInterpolation(matching[0].value, matching[0].sourceSpan, parser);
return new Message(value, meaning(attr.value), description(attr.value));
} else {
throw new I18nError(p.sourceSpan, `Missing attribute '${expectedName}'.`);
}
}
export function removeInterpolation(value: string, source: ParseSourceSpan,
parser: Parser): string {
try {
let parsed = parser.splitInterpolation(value, source.toString());
let usedNames = new Map<string, number>();
if (isPresent(parsed)) {
let res = "";
for (let i = 0; i < parsed.strings.length; ++i) {
res += parsed.strings[i];
if (i != parsed.strings.length - 1) {
let customPhName = getPhNameFromBinding(parsed.expressions[i], i);
customPhName = dedupePhName(usedNames, customPhName);
res += `<ph name="${customPhName}"/>`;
}
}
return res;
} else {
return value;
}
} catch (e) {
return value;
}
}
export function getPhNameFromBinding(input: string, index: number): string {
let customPhMatch = StringWrapper.split(input, CUSTOM_PH_EXP);
return customPhMatch.length > 1 ? customPhMatch[1] : `${index}`;
}
export function dedupePhName(usedNames: Map<string, number>, name: string): string {
let duplicateNameCount = usedNames.get(name);
if (isPresent(duplicateNameCount)) {
usedNames.set(name, duplicateNameCount + 1);
return `${name}_${duplicateNameCount}`;
} else {
usedNames.set(name, 1);
return name;
}
}
export function stringifyNodes(nodes: HtmlAst[], parser: Parser): string {
let visitor = new _StringifyVisitor(parser);
return htmlVisitAll(visitor, nodes).join("");
}
class _StringifyVisitor implements HtmlAstVisitor {
private _index: number = 0;
constructor(private _parser: Parser) {}
visitElement(ast: HtmlElementAst, context: any): any {
let name = this._index++;
let children = this._join(htmlVisitAll(this, ast.children), "");
return `<ph name="e${name}">${children}</ph>`;
}
visitAttr(ast: HtmlAttrAst, context: any): any { return null; }
visitText(ast: HtmlTextAst, context: any): any {
let index = this._index++;
let noInterpolation = removeInterpolation(ast.value, ast.sourceSpan, this._parser);
if (noInterpolation != ast.value) {
return `<ph name="t${index}">${noInterpolation}</ph>`;
} else {
return ast.value;
}
}
visitComment(ast: HtmlCommentAst, context: any): any { return ""; }
visitExpansion(ast: HtmlExpansionAst, context: any): any { return null; }
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
private _join(strs: string[], str: string): string {
return strs.filter(s => s.length > 0).join(str);
}
}

View File

@ -1,95 +0,0 @@
import {isPresent, isBlank, RegExpWrapper} from '../../src/facade/lang';
import {HtmlAst, HtmlElementAst} from '../html_ast';
import {Message, id} from './message';
import {HtmlParser} from '../html_parser';
import {ParseSourceSpan, ParseError} from '../parse_util';
let _PLACEHOLDER_REGEXP = RegExpWrapper.create(`\\<ph(\\s)+name=("(\\w)+")\\/\\>`);
const _ID_ATTR = "id";
const _MSG_ELEMENT = "msg";
const _BUNDLE_ELEMENT = "message-bundle";
export function serializeXmb(messages: Message[]): string {
let ms = messages.map((m) => _serializeMessage(m)).join("");
return `<message-bundle>${ms}</message-bundle>`;
}
export class XmbDeserializationResult {
constructor(public content: string, public messages: {[key: string]: HtmlAst[]},
public errors: ParseError[]) {}
}
export class XmbDeserializationError extends ParseError {
constructor(span: ParseSourceSpan, msg: string) { super(span, msg); }
}
export function deserializeXmb(content: string, url: string): XmbDeserializationResult {
let parser = new HtmlParser();
let normalizedContent = _expandPlaceholder(content.trim());
let parsed = parser.parse(normalizedContent, url);
if (parsed.errors.length > 0) {
return new XmbDeserializationResult(null, {}, parsed.errors);
}
if (_checkRootElement(parsed.rootNodes)) {
return new XmbDeserializationResult(
null, {}, [new XmbDeserializationError(null, `Missing element "${_BUNDLE_ELEMENT}"`)]);
}
let bundleEl = <HtmlElementAst>parsed.rootNodes[0]; // test this
let errors = [];
let messages: {[key: string]: HtmlAst[]} = {};
_createMessages(bundleEl.children, messages, errors);
return (errors.length == 0) ?
new XmbDeserializationResult(normalizedContent, messages, []) :
new XmbDeserializationResult(null, <{[key: string]: HtmlAst[]}>{}, errors);
}
function _checkRootElement(nodes: HtmlAst[]): boolean {
return nodes.length < 1 || !(nodes[0] instanceof HtmlElementAst) ||
(<HtmlElementAst>nodes[0]).name != _BUNDLE_ELEMENT;
}
function _createMessages(nodes: HtmlAst[], messages: {[key: string]: HtmlAst[]},
errors: ParseError[]): void {
nodes.forEach((item) => {
if (item instanceof HtmlElementAst) {
let msg = <HtmlElementAst>item;
if (msg.name != _MSG_ELEMENT) {
errors.push(
new XmbDeserializationError(item.sourceSpan, `Unexpected element "${msg.name}"`));
return;
}
let id = _id(msg);
if (isBlank(id)) {
errors.push(
new XmbDeserializationError(item.sourceSpan, `"${_ID_ATTR}" attribute is missing`));
return;
}
messages[id] = msg.children;
}
});
}
function _id(el: HtmlElementAst): string {
let ids = el.attrs.filter(a => a.name == _ID_ATTR);
return ids.length > 0 ? ids[0].value : null;
}
function _serializeMessage(m: Message): string {
let desc = isPresent(m.description) ? ` desc='${m.description}'` : "";
return `<msg id='${id(m)}'${desc}>${m.content}</msg>`;
}
function _expandPlaceholder(input: string): string {
return RegExpWrapper.replaceAll(_PLACEHOLDER_REGEXP, input, (match) => {
let nameWithQuotes = match[2];
return `<ph name=${nameWithQuotes}></ph>`;
});
}

View File

@ -1,212 +0,0 @@
import {
SimpleChange,
ChangeDetectorRef,
ChangeDetectionStrategy,
ElementRef,
ViewContainerRef,
Renderer,
RenderComponentType,
Injector,
QueryList,
ViewEncapsulation,
TemplateRef
} from '@angular/core';
import {SecurityContext} from '../core_private';
import {
AppElement,
AppView,
DebugAppView,
ChangeDetectorState,
checkBinding,
DebugContext,
devModeEqual,
flattenNestedViewRenderNodes,
interpolate,
RenderDebugInfo,
StaticNodeDebugInfo,
TemplateRef_,
uninitialized,
ValueUnwrapper,
ViewType,
ViewUtils,
castByValue,
EMPTY_ARRAY,
EMPTY_MAP,
pureProxy1,
pureProxy2,
pureProxy3,
pureProxy4,
pureProxy5,
pureProxy6,
pureProxy7,
pureProxy8,
pureProxy9,
pureProxy10
} from '../core_private';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {assetUrl} from './util';
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
var VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
var CD_MODULE_URL = assetUrl('core', 'change_detection/change_detection');
// Reassign the imports to different variables so we can
// define static variables with the name of the import.
// (only needed for Dart).
var impViewUtils = ViewUtils;
var impAppView = AppView;
var impDebugAppView = DebugAppView;
var impDebugContext = DebugContext;
var impAppElement = AppElement;
var impElementRef = ElementRef;
var impViewContainerRef = ViewContainerRef;
var impChangeDetectorRef = ChangeDetectorRef;
var impRenderComponentType = RenderComponentType;
var impQueryList = QueryList;
var impTemplateRef = TemplateRef;
var impTemplateRef_ = TemplateRef_;
var impValueUnwrapper = ValueUnwrapper;
var impInjector = Injector;
var impViewEncapsulation = ViewEncapsulation;
var impViewType = ViewType;
var impChangeDetectionStrategy = ChangeDetectionStrategy;
var impStaticNodeDebugInfo = StaticNodeDebugInfo;
var impRenderer = Renderer;
var impSimpleChange = SimpleChange;
var impUninitialized = uninitialized;
var impChangeDetectorState = ChangeDetectorState;
var impFlattenNestedViewRenderNodes = flattenNestedViewRenderNodes;
var impDevModeEqual = devModeEqual;
var impInterpolate = interpolate;
var impCheckBinding = checkBinding;
var impCastByValue = castByValue;
var impEMPTY_ARRAY = EMPTY_ARRAY;
var impEMPTY_MAP = EMPTY_MAP;
export class Identifiers {
static ViewUtils = new CompileIdentifierMetadata(
{name: 'ViewUtils', moduleUrl: assetUrl('core', 'linker/view_utils'), runtime: impViewUtils});
static AppView = new CompileIdentifierMetadata(
{name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: impAppView});
static DebugAppView = new CompileIdentifierMetadata(
{name: 'DebugAppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: impDebugAppView});
static AppElement = new CompileIdentifierMetadata(
{name: 'AppElement', moduleUrl: assetUrl('core', 'linker/element'), runtime: impAppElement});
static ElementRef = new CompileIdentifierMetadata({
name: 'ElementRef',
moduleUrl: assetUrl('core', 'linker/element_ref'),
runtime: impElementRef
});
static ViewContainerRef = new CompileIdentifierMetadata({
name: 'ViewContainerRef',
moduleUrl: assetUrl('core', 'linker/view_container_ref'),
runtime: impViewContainerRef
});
static ChangeDetectorRef = new CompileIdentifierMetadata({
name: 'ChangeDetectorRef',
moduleUrl: assetUrl('core', 'change_detection/change_detector_ref'),
runtime: impChangeDetectorRef
});
static RenderComponentType = new CompileIdentifierMetadata({
name: 'RenderComponentType',
moduleUrl: assetUrl('core', 'render/api'),
runtime: impRenderComponentType
});
static QueryList = new CompileIdentifierMetadata(
{name: 'QueryList', moduleUrl: assetUrl('core', 'linker/query_list'), runtime: impQueryList});
static TemplateRef = new CompileIdentifierMetadata({
name: 'TemplateRef',
moduleUrl: assetUrl('core', 'linker/template_ref'),
runtime: impTemplateRef
});
static TemplateRef_ = new CompileIdentifierMetadata({
name: 'TemplateRef_',
moduleUrl: assetUrl('core', 'linker/template_ref'),
runtime: impTemplateRef_
});
static ValueUnwrapper = new CompileIdentifierMetadata(
{name: 'ValueUnwrapper', moduleUrl: CD_MODULE_URL, runtime: impValueUnwrapper});
static Injector = new CompileIdentifierMetadata(
{name: 'Injector', moduleUrl: assetUrl('core', 'di/injector'), runtime: impInjector});
static ViewEncapsulation = new CompileIdentifierMetadata({
name: 'ViewEncapsulation',
moduleUrl: assetUrl('core', 'metadata/view'),
runtime: impViewEncapsulation
});
static ViewType = new CompileIdentifierMetadata(
{name: 'ViewType', moduleUrl: assetUrl('core', 'linker/view_type'), runtime: impViewType});
static ChangeDetectionStrategy = new CompileIdentifierMetadata({
name: 'ChangeDetectionStrategy',
moduleUrl: CD_MODULE_URL,
runtime: impChangeDetectionStrategy
});
static StaticNodeDebugInfo = new CompileIdentifierMetadata({
name: 'StaticNodeDebugInfo',
moduleUrl: assetUrl('core', 'linker/debug_context'),
runtime: impStaticNodeDebugInfo
});
static DebugContext = new CompileIdentifierMetadata({
name: 'DebugContext',
moduleUrl: assetUrl('core', 'linker/debug_context'),
runtime: impDebugContext
});
static Renderer = new CompileIdentifierMetadata(
{name: 'Renderer', moduleUrl: assetUrl('core', 'render/api'), runtime: impRenderer});
static SimpleChange = new CompileIdentifierMetadata(
{name: 'SimpleChange', moduleUrl: CD_MODULE_URL, runtime: impSimpleChange});
static uninitialized = new CompileIdentifierMetadata(
{name: 'uninitialized', moduleUrl: CD_MODULE_URL, runtime: impUninitialized});
static ChangeDetectorState = new CompileIdentifierMetadata(
{name: 'ChangeDetectorState', moduleUrl: CD_MODULE_URL, runtime: impChangeDetectorState});
static checkBinding = new CompileIdentifierMetadata(
{name: 'checkBinding', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impCheckBinding});
static flattenNestedViewRenderNodes = new CompileIdentifierMetadata({
name: 'flattenNestedViewRenderNodes',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: impFlattenNestedViewRenderNodes
});
static devModeEqual = new CompileIdentifierMetadata(
{name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: impDevModeEqual});
static interpolate = new CompileIdentifierMetadata(
{name: 'interpolate', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impInterpolate});
static castByValue = new CompileIdentifierMetadata(
{name: 'castByValue', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impCastByValue});
static EMPTY_ARRAY = new CompileIdentifierMetadata(
{name: 'EMPTY_ARRAY', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impEMPTY_ARRAY});
static EMPTY_MAP = new CompileIdentifierMetadata(
{name: 'EMPTY_MAP', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impEMPTY_MAP});
static pureProxies = [
null,
new CompileIdentifierMetadata(
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy1}),
new CompileIdentifierMetadata(
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy2}),
new CompileIdentifierMetadata(
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy3}),
new CompileIdentifierMetadata(
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy4}),
new CompileIdentifierMetadata(
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy5}),
new CompileIdentifierMetadata(
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy6}),
new CompileIdentifierMetadata(
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy7}),
new CompileIdentifierMetadata(
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy8}),
new CompileIdentifierMetadata(
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy9}),
new CompileIdentifierMetadata(
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy10}),
];
static SecurityContext = new CompileIdentifierMetadata({
name: 'SecurityContext',
moduleUrl: assetUrl('core', 'security'),
runtime: SecurityContext,
});
}
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
return new CompileTokenMetadata({identifier: identifier});
}

View File

@ -1,436 +0,0 @@
import {
AttributeMetadata,
ReflectiveDependency,
OptionalMetadata,
ComponentMetadata,
SelfMetadata,
HostMetadata,
SkipSelfMetadata,
Provider,
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
reflector,
Injectable,
Inject,
Optional,
ViewMetadata,
NoAnnotationError,
QueryMetadata,
resolveForwardRef,
InjectMetadata,
ViewQueryMetadata
} from '@angular/core';
import {constructDependencies, LIFECYCLE_HOOKS_VALUES, ReflectorReader} from '../core_private';
import {
Type,
isBlank,
isPresent,
isArray,
stringify,
isString,
isStringMap
} from '../src/facade/lang';
import {StringMapWrapper} from '../src/facade/collection';
import {BaseException} from '../src/facade/exceptions';
import * as cpl from './compile_metadata';
import {DirectiveResolver} from './directive_resolver';
import {PipeResolver} from './pipe_resolver';
import {ViewResolver} from './view_resolver';
import {hasLifecycleHook} from './directive_lifecycle_reflector';
import {MODULE_SUFFIX, sanitizeIdentifier, ValueTransformer, visitValue} from './util';
import {assertArrayOfStrings} from './assertions';
import {getUrlScheme} from './url_resolver';
import {createProvider, isProviderLiteral} from "../core_private";
@Injectable()
export class CompileMetadataResolver {
private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>();
private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>();
private _anonymousTypes = new Map<Object, number>();
private _anonymousTypeIndex = 0;
private _reflector: ReflectorReader;
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
private _viewResolver: ViewResolver,
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[],
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[],
_reflector?: ReflectorReader) {
if (isPresent(_reflector)) {
this._reflector = _reflector;
} else {
this._reflector = reflector;
}
}
private sanitizeTokenName(token: any): string {
let identifier = stringify(token);
if (identifier.indexOf('(') >= 0) {
// case: anonymous functions!
let found = this._anonymousTypes.get(token);
if (isBlank(found)) {
this._anonymousTypes.set(token, this._anonymousTypeIndex++);
found = this._anonymousTypes.get(token);
}
identifier = `anonymous_token_${found}_`;
}
return sanitizeIdentifier(identifier);
}
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
var meta = this._directiveCache.get(directiveType);
if (isBlank(meta)) {
var dirMeta = this._directiveResolver.resolve(directiveType);
var templateMeta = null;
var changeDetectionStrategy = null;
var viewProviders = [];
var moduleUrl = staticTypeModuleUrl(directiveType);
if (dirMeta instanceof ComponentMetadata) {
assertArrayOfStrings('styles', dirMeta.styles);
var cmpMeta = <ComponentMetadata>dirMeta;
var viewMeta = this._viewResolver.resolve(directiveType);
assertArrayOfStrings('styles', viewMeta.styles);
templateMeta = new cpl.CompileTemplateMetadata({
encapsulation: viewMeta.encapsulation,
template: viewMeta.template,
templateUrl: viewMeta.templateUrl,
styles: viewMeta.styles,
styleUrls: viewMeta.styleUrls
});
changeDetectionStrategy = cmpMeta.changeDetection;
if (isPresent(dirMeta.viewProviders)) {
viewProviders = this.getProvidersMetadata(dirMeta.viewProviders);
}
moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta);
}
var providers = [];
if (isPresent(dirMeta.providers)) {
providers = this.getProvidersMetadata(dirMeta.providers);
}
var queries = [];
var viewQueries = [];
if (isPresent(dirMeta.queries)) {
queries = this.getQueriesMetadata(dirMeta.queries, false);
viewQueries = this.getQueriesMetadata(dirMeta.queries, true);
}
meta = cpl.CompileDirectiveMetadata.create({
selector: dirMeta.selector,
exportAs: dirMeta.exportAs,
isComponent: isPresent(templateMeta),
type: this.getTypeMetadata(directiveType, moduleUrl),
template: templateMeta,
changeDetection: changeDetectionStrategy,
inputs: dirMeta.inputs,
outputs: dirMeta.outputs,
host: dirMeta.host,
lifecycleHooks:
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType)),
providers: providers,
viewProviders: viewProviders,
queries: queries,
viewQueries: viewQueries
});
this._directiveCache.set(directiveType, meta);
}
return meta;
}
/**
* @param someType a symbol which may or may not be a directive type
* @returns {cpl.CompileDirectiveMetadata} if possible, otherwise null.
*/
maybeGetDirectiveMetadata(someType: Type): cpl.CompileDirectiveMetadata {
try {
return this.getDirectiveMetadata(someType);
} catch (e) {
if (e.message.indexOf('No Directive annotation') !== -1) {
return null;
}
throw e;
}
}
getTypeMetadata(type: Type, moduleUrl: string): cpl.CompileTypeMetadata {
return new cpl.CompileTypeMetadata({
name: this.sanitizeTokenName(type),
moduleUrl: moduleUrl,
runtime: type,
diDeps: this.getDependenciesMetadata(type, null)
});
}
getFactoryMetadata(factory: Function, moduleUrl: string): cpl.CompileFactoryMetadata {
return new cpl.CompileFactoryMetadata({
name: this.sanitizeTokenName(factory),
moduleUrl: moduleUrl,
runtime: factory,
diDeps: this.getDependenciesMetadata(factory, null)
});
}
getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
var meta = this._pipeCache.get(pipeType);
if (isBlank(meta)) {
var pipeMeta = this._pipeResolver.resolve(pipeType);
meta = new cpl.CompilePipeMetadata({
type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
name: pipeMeta.name,
pure: pipeMeta.pure,
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, pipeType)),
});
this._pipeCache.set(pipeType, meta);
}
return meta;
}
getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] {
var view = this._viewResolver.resolve(component);
var directives = flattenDirectives(view, this._platformDirectives);
for (var i = 0; i < directives.length; i++) {
if (!isValidType(directives[i])) {
throw new BaseException(
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
}
}
return directives.map(type => this.getDirectiveMetadata(type));
}
getViewPipesMetadata(component: Type): cpl.CompilePipeMetadata[] {
var view = this._viewResolver.resolve(component);
var pipes = flattenPipes(view, this._platformPipes);
for (var i = 0; i < pipes.length; i++) {
if (!isValidType(pipes[i])) {
throw new BaseException(
`Unexpected piped value '${stringify(pipes[i])}' on the View of component '${stringify(component)}'`);
}
}
return pipes.map(type => this.getPipeMetadata(type));
}
getDependenciesMetadata(typeOrFunc: Type | Function,
dependencies: any[]): cpl.CompileDiDependencyMetadata[] {
let params = isPresent(dependencies) ? dependencies : this._reflector.parameters(typeOrFunc);
if (isBlank(params)) {
params = [];
}
return params.map((param) => {
if (isBlank(param)) {
return null;
}
let isAttribute = false;
let isHost = false;
let isSelf = false;
let isSkipSelf = false;
let isOptional = false;
let query: QueryMetadata = null;
let viewQuery: ViewQueryMetadata = null;
var token = null;
if (isArray(param)) {
(<any[]>param)
.forEach((paramEntry) => {
if (paramEntry instanceof HostMetadata) {
isHost = true;
} else if (paramEntry instanceof SelfMetadata) {
isSelf = true;
} else if (paramEntry instanceof SkipSelfMetadata) {
isSkipSelf = true;
} else if (paramEntry instanceof OptionalMetadata) {
isOptional = true;
} else if (paramEntry instanceof AttributeMetadata) {
isAttribute = true;
token = paramEntry.attributeName;
} else if (paramEntry instanceof QueryMetadata) {
if (paramEntry.isViewQuery) {
viewQuery = paramEntry;
} else {
query = paramEntry;
}
} else if (paramEntry instanceof InjectMetadata) {
token = paramEntry.token;
} else if (isValidType(paramEntry) && isBlank(token)) {
token = paramEntry;
}
});
} else {
token = param;
}
if (isBlank(token)) {
return null;
}
return new cpl.CompileDiDependencyMetadata({
isAttribute: isAttribute,
isHost: isHost,
isSelf: isSelf,
isSkipSelf: isSkipSelf,
isOptional: isOptional,
query: isPresent(query) ? this.getQueryMetadata(query, null) : null,
viewQuery: isPresent(viewQuery) ? this.getQueryMetadata(viewQuery, null) : null,
token: this.getTokenMetadata(token)
});
});
}
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
token = resolveForwardRef(token);
var compileToken;
if (isString(token)) {
compileToken = new cpl.CompileTokenMetadata({value: token});
} else {
compileToken = new cpl.CompileTokenMetadata({
identifier: new cpl.CompileIdentifierMetadata({
runtime: token,
name: this.sanitizeTokenName(token),
moduleUrl: staticTypeModuleUrl(token)
})
});
}
return compileToken;
}
getProvidersMetadata(providers: any[]):
Array<cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any[]> {
return providers.map((provider) => {
provider = resolveForwardRef(provider);
if (isArray(provider)) {
return this.getProvidersMetadata(provider);
} else if (provider instanceof Provider) {
return this.getProviderMetadata(provider);
} else if (isProviderLiteral(provider)) {
return this.getProviderMetadata(createProvider(provider));
} else {
return this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
}
});
}
getProviderMetadata(provider: Provider): cpl.CompileProviderMetadata {
var compileDeps;
if (isPresent(provider.useClass)) {
compileDeps = this.getDependenciesMetadata(provider.useClass, provider.dependencies);
} else if (isPresent(provider.useFactory)) {
compileDeps = this.getDependenciesMetadata(provider.useFactory, provider.dependencies);
}
return new cpl.CompileProviderMetadata({
token: this.getTokenMetadata(provider.token),
useClass:
isPresent(provider.useClass) ?
this.getTypeMetadata(provider.useClass, staticTypeModuleUrl(provider.useClass)) :
null,
useValue: convertToCompileValue(provider.useValue),
useFactory: isPresent(provider.useFactory) ?
this.getFactoryMetadata(provider.useFactory,
staticTypeModuleUrl(provider.useFactory)) :
null,
useExisting: isPresent(provider.useExisting) ? this.getTokenMetadata(provider.useExisting) :
null,
deps: compileDeps,
multi: provider.multi
});
}
getQueriesMetadata(queries: {[key: string]: QueryMetadata},
isViewQuery: boolean): cpl.CompileQueryMetadata[] {
var compileQueries = [];
StringMapWrapper.forEach(queries, (query, propertyName) => {
if (query.isViewQuery === isViewQuery) {
compileQueries.push(this.getQueryMetadata(query, propertyName));
}
});
return compileQueries;
}
getQueryMetadata(q: QueryMetadata, propertyName: string): cpl.CompileQueryMetadata {
var selectors;
if (q.isVarBindingQuery) {
selectors = q.varBindings.map(varName => this.getTokenMetadata(varName));
} else {
selectors = [this.getTokenMetadata(q.selector)];
}
return new cpl.CompileQueryMetadata({
selectors: selectors,
first: q.first,
descendants: q.descendants,
propertyName: propertyName,
read: isPresent(q.read) ? this.getTokenMetadata(q.read) : null
});
}
}
function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[] {
let directives = [];
if (isPresent(platformDirectives)) {
flattenArray(platformDirectives, directives);
}
if (isPresent(view.directives)) {
flattenArray(view.directives, directives);
}
return directives;
}
function flattenPipes(view: ViewMetadata, platformPipes: any[]): Type[] {
let pipes = [];
if (isPresent(platformPipes)) {
flattenArray(platformPipes, pipes);
}
if (isPresent(view.pipes)) {
flattenArray(view.pipes, pipes);
}
return pipes;
}
function flattenArray(tree: any[], out: Array<Type | any[]>): void {
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);
}
}
}
function isStaticType(value: any): boolean {
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
}
function isValidType(value: any): boolean {
return isStaticType(value) || (value instanceof Type);
}
function staticTypeModuleUrl(value: any): string {
return isStaticType(value) ? value['filePath'] : null;
}
function componentModuleUrl(reflector: ReflectorReader, type: any,
cmpMetadata: ComponentMetadata): string {
if (isStaticType(type)) {
return staticTypeModuleUrl(type);
}
if (isPresent(cmpMetadata.moduleId)) {
var moduleId = cmpMetadata.moduleId;
var scheme = getUrlScheme(moduleId);
return isPresent(scheme) && scheme.length > 0 ? moduleId :
`package:${moduleId}${MODULE_SUFFIX}`;
}
return reflector.importUri(type);
}
// Only fill CompileIdentifierMetadata.runtime if needed...
function convertToCompileValue(value: any): any {
return visitValue(value, new _CompileValueConverter(), null);
}
class _CompileValueConverter extends ValueTransformer {
visitOther(value: any, context: any): any {
if (isStaticType(value)) {
return new cpl.CompileIdentifierMetadata(
{name: value['name'], moduleUrl: staticTypeModuleUrl(value)});
} else {
return new cpl.CompileIdentifierMetadata({runtime: value});
}
}
}

View File

@ -1,171 +0,0 @@
import {ComponentFactory} from '@angular/core';
import {
CompileDirectiveMetadata,
CompileIdentifierMetadata,
CompilePipeMetadata,
createHostComponentMeta
} from './compile_metadata';
import {BaseException} from '../src/facade/exceptions';
import {ListWrapper} from '../src/facade/collection';
import {StyleCompiler, StylesCompileResult} from './style_compiler';
import {ViewCompiler, ViewCompileResult} from './view_compiler/view_compiler';
import {TemplateParser} from './template_parser';
import {DirectiveNormalizer} from './directive_normalizer';
import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast';
import {XHR} from './xhr';
import {
MODULE_SUFFIX,
assetUrl,
} from './util';
var _COMPONENT_FACTORY_IDENTIFIER = new CompileIdentifierMetadata({
name: 'ComponentFactory',
runtime: ComponentFactory,
moduleUrl: assetUrl('core', 'linker/component_factory')
});
export class SourceModule {
constructor(public moduleUrl: string, public source: string) {}
}
export class StyleSheetSourceWithImports {
constructor(public source: SourceModule, public importedUrls: string[]) {}
}
export class NormalizedComponentWithViewDirectives {
constructor(public component: CompileDirectiveMetadata,
public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
}
export class OfflineCompiler {
constructor(private _directiveNormalizer: DirectiveNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _outputEmitter: OutputEmitter,
private _xhr: XHR) {}
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
Promise<CompileDirectiveMetadata> {
return this._directiveNormalizer.normalizeDirective(directive);
}
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule {
if (components.length === 0) {
throw new BaseException('No components given');
}
var statements = [];
var exportedVars = [];
var moduleUrl = _templateModuleUrl(components[0].component);
components.forEach(componentWithDirs => {
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
_assertComponent(compMeta);
var compViewFactoryVar = this._compileComponent(compMeta, componentWithDirs.directives,
componentWithDirs.pipes, statements);
exportedVars.push(compViewFactoryVar);
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
var hostViewFactoryVar = this._compileComponent(hostMeta, [compMeta], [], statements);
var compFactoryVar = `${compMeta.type.name}NgFactory`;
statements.push(
o.variable(compFactoryVar)
.set(o.importExpr(_COMPONENT_FACTORY_IDENTIFIER, [o.importType(compMeta.type)])
.instantiate(
[
o.literal(compMeta.selector),
o.variable(hostViewFactoryVar),
o.importExpr(compMeta.type)
],
o.importType(_COMPONENT_FACTORY_IDENTIFIER,
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]));
exportedVars.push(compFactoryVar);
});
return this._codegenSourceModule(moduleUrl, statements, exportedVars);
}
loadAndCompileStylesheet(stylesheetUrl: string, shim: boolean,
suffix: string): Promise<StyleSheetSourceWithImports> {
return this._xhr.get(stylesheetUrl)
.then((cssText) => {
var compileResult = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, shim);
var importedUrls = [];
compileResult.dependencies.forEach((dep) => {
importedUrls.push(dep.moduleUrl);
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, suffix);
});
return new StyleSheetSourceWithImports(
this._codgenStyles(stylesheetUrl, shim, suffix, compileResult), importedUrls);
});
}
private _compileComponent(compMeta: CompileDirectiveMetadata,
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
targetStatements: o.Statement[]): string {
var styleResult = this._styleCompiler.compileComponent(compMeta);
var parsedTemplate = this._templateParser.parse(compMeta, compMeta.template.template,
directives, pipes, compMeta.type.name);
var viewResult = this._viewCompiler.compileComponent(compMeta, parsedTemplate,
o.variable(styleResult.stylesVar), pipes);
ListWrapper.addAll(targetStatements,
_resolveStyleStatements(compMeta.type.moduleUrl, styleResult));
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
return viewResult.viewFactoryVar;
}
private _codgenStyles(inputUrl: string, shim: boolean, suffix: string,
stylesCompileResult: StylesCompileResult): SourceModule {
return this._codegenSourceModule(_stylesModuleUrl(inputUrl, shim, suffix),
stylesCompileResult.statements,
[stylesCompileResult.stylesVar]);
}
private _codegenSourceModule(moduleUrl: string, statements: o.Statement[],
exportedVars: string[]): SourceModule {
return new SourceModule(
moduleUrl, this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
}
}
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach(
(dep) => { dep.factoryPlaceholder.moduleUrl = _templateModuleUrl(dep.comp); });
return compileResult.statements;
}
function _resolveStyleStatements(containingModuleUrl: string,
compileResult: StylesCompileResult): o.Statement[] {
var containingSuffix = _splitSuffix(containingModuleUrl)[1];
compileResult.dependencies.forEach((dep) => {
dep.valuePlaceholder.moduleUrl =
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, containingSuffix);
});
return compileResult.statements;
}
function _templateModuleUrl(comp: CompileDirectiveMetadata): string {
var urlWithSuffix = _splitSuffix(comp.type.moduleUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
return shim ? `${stylesheetUrl}.shim${suffix}` : `${stylesheetUrl}${suffix}`;
}
function _assertComponent(meta: CompileDirectiveMetadata) {
if (!meta.isComponent) {
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
}
}
function _splitSuffix(path: string): string[] {
let lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
} else {
return [path, ''];
}
}

View File

@ -1,418 +0,0 @@
import {isPresent, isBlank, isString, StringWrapper} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import * as o from './output_ast';
var _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
export var CATCH_ERROR_VAR = o.variable('error');
export var CATCH_STACK_VAR = o.variable('stack');
export abstract class OutputEmitter {
abstract emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string;
}
class _EmittedLine {
parts: string[] = [];
constructor(public indent: number) {}
}
export class EmitterVisitorContext {
static createRoot(exportedVars: string[]): EmitterVisitorContext {
return new EmitterVisitorContext(exportedVars, 0);
}
private _lines: _EmittedLine[];
private _classes: o.ClassStmt[] = [];
constructor(private _exportedVars: string[], private _indent: number) {
this._lines = [new _EmittedLine(_indent)];
}
private get _currentLine(): _EmittedLine { return this._lines[this._lines.length - 1]; }
isExportedVar(varName: string): boolean { return this._exportedVars.indexOf(varName) !== -1; }
println(lastPart: string = ''): void { this.print(lastPart, true); }
lineIsEmpty(): boolean { return this._currentLine.parts.length === 0; }
print(part: string, newLine: boolean = false) {
if (part.length > 0) {
this._currentLine.parts.push(part);
}
if (newLine) {
this._lines.push(new _EmittedLine(this._indent));
}
}
removeEmptyLastLine() {
if (this.lineIsEmpty()) {
this._lines.pop();
}
}
incIndent() {
this._indent++;
this._currentLine.indent = this._indent;
}
decIndent() {
this._indent--;
this._currentLine.indent = this._indent;
}
pushClass(clazz: o.ClassStmt) { this._classes.push(clazz); }
popClass(): o.ClassStmt { return this._classes.pop(); }
get currentClass(): o.ClassStmt {
return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
}
toSource(): any {
var lines = this._lines;
if (lines[lines.length - 1].parts.length === 0) {
lines = lines.slice(0, lines.length - 1);
}
return lines.map((line) => {
if (line.parts.length > 0) {
return _createIndent(line.indent) + line.parts.join('');
} else {
return '';
}
})
.join('\n');
}
}
export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
constructor(private _escapeDollarInStrings: boolean) {}
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
stmt.expr.visitExpression(this, ctx);
ctx.println(';');
return null;
}
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
ctx.print(`return `);
stmt.value.visitExpression(this, ctx);
ctx.println(';');
return null;
}
abstract visitCastExpr(ast: o.CastExpr, context: any): any;
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
ctx.print(`if (`);
stmt.condition.visitExpression(this, ctx);
ctx.print(`) {`);
var hasElseCase = isPresent(stmt.falseCase) && stmt.falseCase.length > 0;
if (stmt.trueCase.length <= 1 && !hasElseCase) {
ctx.print(` `);
this.visitAllStatements(stmt.trueCase, ctx);
ctx.removeEmptyLastLine();
ctx.print(` `);
} else {
ctx.println();
ctx.incIndent();
this.visitAllStatements(stmt.trueCase, ctx);
ctx.decIndent();
if (hasElseCase) {
ctx.println(`} else {`);
ctx.incIndent();
this.visitAllStatements(stmt.falseCase, ctx);
ctx.decIndent();
}
}
ctx.println(`}`);
return null;
}
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
ctx.print(`throw `);
stmt.error.visitExpression(this, ctx);
ctx.println(`;`);
return null;
}
visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
var lines = stmt.comment.split('\n');
lines.forEach((line) => { ctx.println(`// ${line}`); });
return null;
}
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
visitWriteVarExpr(expr: o.WriteVarExpr, ctx: EmitterVisitorContext): any {
var lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) {
ctx.print('(');
}
ctx.print(`${expr.name} = `);
expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) {
ctx.print(')');
}
return null;
}
visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: EmitterVisitorContext): any {
var lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) {
ctx.print('(');
}
expr.receiver.visitExpression(this, ctx);
ctx.print(`[`);
expr.index.visitExpression(this, ctx);
ctx.print(`] = `);
expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) {
ctx.print(')');
}
return null;
}
visitWritePropExpr(expr: o.WritePropExpr, ctx: EmitterVisitorContext): any {
var lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) {
ctx.print('(');
}
expr.receiver.visitExpression(this, ctx);
ctx.print(`.${expr.name} = `);
expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) {
ctx.print(')');
}
return null;
}
visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: EmitterVisitorContext): any {
expr.receiver.visitExpression(this, ctx);
var name = expr.name;
if (isPresent(expr.builtin)) {
name = this.getBuiltinMethodName(expr.builtin);
if (isBlank(name)) {
// some builtins just mean to skip the call.
// e.g. `bind` in Dart.
return null;
}
}
ctx.print(`.${name}(`);
this.visitAllExpressions(expr.args, ctx, `,`);
ctx.print(`)`);
return null;
}
abstract getBuiltinMethodName(method: o.BuiltinMethod): string;
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): any {
expr.fn.visitExpression(this, ctx);
ctx.print(`(`);
this.visitAllExpressions(expr.args, ctx, ',');
ctx.print(`)`);
return null;
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any {
var varName = ast.name;
if (isPresent(ast.builtin)) {
switch (ast.builtin) {
case o.BuiltinVar.Super:
varName = 'super';
break;
case o.BuiltinVar.This:
varName = 'this';
break;
case o.BuiltinVar.CatchError:
varName = CATCH_ERROR_VAR.name;
break;
case o.BuiltinVar.CatchStack:
varName = CATCH_STACK_VAR.name;
break;
default:
throw new BaseException(`Unknown builtin variable ${ast.builtin}`);
}
}
ctx.print(varName);
return null;
}
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
ctx.print(`new `);
ast.classExpr.visitExpression(this, ctx);
ctx.print(`(`);
this.visitAllExpressions(ast.args, ctx, ',');
ctx.print(`)`);
return null;
}
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
var value = ast.value;
if (isString(value)) {
ctx.print(escapeSingleQuoteString(value, this._escapeDollarInStrings));
} else if (isBlank(value)) {
ctx.print('null');
} else {
ctx.print(`${value}`);
}
return null;
}
abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any;
visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`);
ast.condition.visitExpression(this, ctx);
ctx.print('? ');
ast.trueCase.visitExpression(this, ctx);
ctx.print(': ');
ast.falseCase.visitExpression(this, ctx);
ctx.print(`)`);
return null;
}
visitNotExpr(ast: o.NotExpr, ctx: EmitterVisitorContext): any {
ctx.print('!');
ast.condition.visitExpression(this, ctx);
return null;
}
abstract visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any;
abstract visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: any): any;
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
var opStr;
switch (ast.operator) {
case o.BinaryOperator.Equals:
opStr = '==';
break;
case o.BinaryOperator.Identical:
opStr = '===';
break;
case o.BinaryOperator.NotEquals:
opStr = '!=';
break;
case o.BinaryOperator.NotIdentical:
opStr = '!==';
break;
case o.BinaryOperator.And:
opStr = '&&';
break;
case o.BinaryOperator.Or:
opStr = '||';
break;
case o.BinaryOperator.Plus:
opStr = '+';
break;
case o.BinaryOperator.Minus:
opStr = '-';
break;
case o.BinaryOperator.Divide:
opStr = '/';
break;
case o.BinaryOperator.Multiply:
opStr = '*';
break;
case o.BinaryOperator.Modulo:
opStr = '%';
break;
case o.BinaryOperator.Lower:
opStr = '<';
break;
case o.BinaryOperator.LowerEquals:
opStr = '<=';
break;
case o.BinaryOperator.Bigger:
opStr = '>';
break;
case o.BinaryOperator.BiggerEquals:
opStr = '>=';
break;
default:
throw new BaseException(`Unknown operator ${ast.operator}`);
}
ctx.print(`(`);
ast.lhs.visitExpression(this, ctx);
ctx.print(` ${opStr} `);
ast.rhs.visitExpression(this, ctx);
ctx.print(`)`);
return null;
}
visitReadPropExpr(ast: o.ReadPropExpr, ctx: EmitterVisitorContext): any {
ast.receiver.visitExpression(this, ctx);
ctx.print(`.`);
ctx.print(ast.name);
return null;
}
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: EmitterVisitorContext): any {
ast.receiver.visitExpression(this, ctx);
ctx.print(`[`);
ast.index.visitExpression(this, ctx);
ctx.print(`]`);
return null;
}
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
var useNewLine = ast.entries.length > 1;
ctx.print(`[`, useNewLine);
ctx.incIndent();
this.visitAllExpressions(ast.entries, ctx, ',', useNewLine);
ctx.decIndent();
ctx.print(`]`, useNewLine);
return null;
}
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
var useNewLine = ast.entries.length > 1;
ctx.print(`{`, useNewLine);
ctx.incIndent();
this.visitAllObjects((entry) => {
ctx.print(`${escapeSingleQuoteString(entry[0], this._escapeDollarInStrings)}: `);
entry[1].visitExpression(this, ctx);
}, ast.entries, ctx, ',', useNewLine);
ctx.decIndent();
ctx.print(`}`, useNewLine);
return null;
}
visitAllExpressions(expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string,
newLine: boolean = false): void {
this.visitAllObjects((expr) => expr.visitExpression(this, ctx), expressions, ctx, separator,
newLine);
}
visitAllObjects(handler: Function, expressions: any, ctx: EmitterVisitorContext,
separator: string, newLine: boolean = false): void {
for (var i = 0; i < expressions.length; i++) {
if (i > 0) {
ctx.print(separator, newLine);
}
handler(expressions[i]);
}
if (newLine) {
ctx.println();
}
}
visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void {
statements.forEach((stmt) => { return stmt.visitStatement(this, ctx); });
}
}
export function escapeSingleQuoteString(input: string, escapeDollar: boolean): any {
if (isBlank(input)) {
return null;
}
var body = StringWrapper.replaceAllMapped(input, _SINGLE_QUOTE_ESCAPE_STRING_RE, (match) => {
if (match[0] == '$') {
return escapeDollar ? '\\$' : '$';
} else if (match[0] == '\n') {
return '\\n';
} else if (match[0] == '\r') {
return '\\r';
} else {
return `\\${match[0]}`;
}
});
return `'${body}'`;
}
function _createIndent(count: number): string {
var res = '';
for (var i = 0; i < count; i++) {
res += ' ';
}
return res;
}

View File

@ -1,164 +0,0 @@
import {isPresent} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import * as o from './output_ast';
import {
EmitterVisitorContext,
AbstractEmitterVisitor,
CATCH_ERROR_VAR,
CATCH_STACK_VAR
} from './abstract_emitter';
export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
constructor() { super(false); }
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
ctx.pushClass(stmt);
this._visitClassConstructor(stmt, ctx);
if (isPresent(stmt.parent)) {
ctx.print(`${stmt.name}.prototype = Object.create(`);
stmt.parent.visitExpression(this, ctx);
ctx.println(`.prototype);`);
}
stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
ctx.popClass();
return null;
}
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`function ${stmt.name}(`);
if (isPresent(stmt.constructorMethod)) {
this._visitParams(stmt.constructorMethod.params, ctx);
}
ctx.println(`) {`);
ctx.incIndent();
if (isPresent(stmt.constructorMethod)) {
if (stmt.constructorMethod.body.length > 0) {
ctx.println(`var self = this;`);
this.visitAllStatements(stmt.constructorMethod.body, ctx);
}
}
ctx.decIndent();
ctx.println(`}`);
}
private _visitClassGetter(stmt: o.ClassStmt, getter: o.ClassGetter, ctx: EmitterVisitorContext) {
ctx.println(
`Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
ctx.incIndent();
if (getter.body.length > 0) {
ctx.println(`var self = this;`);
this.visitAllStatements(getter.body, ctx);
}
ctx.decIndent();
ctx.println(`}});`);
}
private _visitClassMethod(stmt: o.ClassStmt, method: o.ClassMethod, ctx: EmitterVisitorContext) {
ctx.print(`${stmt.name}.prototype.${method.name} = function(`);
this._visitParams(method.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
if (method.body.length > 0) {
ctx.println(`var self = this;`);
this.visitAllStatements(method.body, ctx);
}
ctx.decIndent();
ctx.println(`};`);
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string {
if (ast.builtin === o.BuiltinVar.This) {
ctx.print('self');
} else if (ast.builtin === o.BuiltinVar.Super) {
throw new BaseException(
`'super' needs to be handled at a parent ast node, not at the variable level!`);
} else {
super.visitReadVarExpr(ast, ctx);
}
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
ctx.print(`var ${stmt.name} = `);
stmt.value.visitExpression(this, ctx);
ctx.println(`;`);
return null;
}
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
ast.value.visitExpression(this, ctx);
return null;
}
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): string {
var fnExpr = expr.fn;
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
ctx.currentClass.parent.visitExpression(this, ctx);
ctx.print(`.call(this`);
if (expr.args.length > 0) {
ctx.print(`, `);
this.visitAllExpressions(expr.args, ctx, ',');
}
ctx.print(`)`);
} else {
super.visitInvokeFunctionExpr(expr, ctx);
}
return null;
}
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`function(`);
this._visitParams(ast.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
this.visitAllStatements(ast.statements, ctx);
ctx.decIndent();
ctx.print(`}`);
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
ctx.print(`function ${stmt.name}(`);
this._visitParams(stmt.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent();
ctx.println(`}`);
return null;
}
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`);
ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.incIndent();
var catchStmts = [
<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack'))
.toDeclStmt(null, [o.StmtModifier.Final])
].concat(stmt.catchStmts);
this.visitAllStatements(catchStmts, ctx);
ctx.decIndent();
ctx.println(`}`);
return null;
}
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects((param) => ctx.print(param.name), params, ctx, ',');
}
getBuiltinMethodName(method: o.BuiltinMethod): string {
var name;
switch (method) {
case o.BuiltinMethod.ConcatArray:
name = 'concat';
break;
case o.BuiltinMethod.SubscribeObservable:
name = 'subscribe';
break;
case o.BuiltinMethod.bind:
name = 'bind';
break;
default:
throw new BaseException(`Unknown builtin method: ${method}`);
}
return name;
}
}

View File

@ -1,374 +0,0 @@
import {isPresent, isBlank, isArray} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {CompileIdentifierMetadata} from '../compile_metadata';
import * as o from './output_ast';
import {
OutputEmitter,
EmitterVisitorContext,
AbstractEmitterVisitor,
CATCH_ERROR_VAR,
CATCH_STACK_VAR,
} from './abstract_emitter';
import {ImportGenerator} from './path_util';
var _debugModuleUrl = 'asset://debug/lib';
export function debugOutputAstAsDart(ast: o.Statement | o.Expression | o.Type | any[]): string {
var converter = new _DartEmitterVisitor(_debugModuleUrl);
var ctx = EmitterVisitorContext.createRoot([]);
var asts: any[];
if (isArray(ast)) {
asts = <any[]>ast;
} else {
asts = [ast];
}
asts.forEach((ast) => {
if (ast instanceof o.Statement) {
ast.visitStatement(converter, ctx);
} else if (ast instanceof o.Expression) {
ast.visitExpression(converter, ctx);
} else if (ast instanceof o.Type) {
ast.visitType(converter, ctx);
} else {
throw new BaseException(`Don't know how to print debug info for ${ast}`);
}
});
return ctx.toSource();
}
export class DartEmitter implements OutputEmitter {
constructor(private _importGenerator: ImportGenerator) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
var srcParts = [];
// Note: We are not creating a library here as Dart does not need it.
// Dart analzyer might complain about it though.
var converter = new _DartEmitterVisitor(moduleUrl);
var ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
srcParts.push(
`import '${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}' as ${prefix};`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
}
}
class _DartEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
importsWithPrefixes = new Map<string, string>();
constructor(private _moduleUrl: string) { super(true); }
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
if (stmt.hasModifier(o.StmtModifier.Final)) {
if (isConstType(stmt.type)) {
ctx.print(`const `);
} else {
ctx.print(`final `);
}
} else if (isBlank(stmt.type)) {
ctx.print(`var `);
}
if (isPresent(stmt.type)) {
stmt.type.visitType(this, ctx);
ctx.print(` `);
}
ctx.print(`${stmt.name} = `);
stmt.value.visitExpression(this, ctx);
ctx.println(`;`);
return null;
}
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`);
ast.value.visitExpression(this, ctx);
ctx.print(` as `);
ast.type.visitType(this, ctx);
ctx.print(`)`);
return null;
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
ctx.pushClass(stmt);
ctx.print(`class ${stmt.name}`);
if (isPresent(stmt.parent)) {
ctx.print(` extends `);
stmt.parent.visitExpression(this, ctx);
}
ctx.println(` {`);
ctx.incIndent();
stmt.fields.forEach((field) => this._visitClassField(field, ctx));
if (isPresent(stmt.constructorMethod)) {
this._visitClassConstructor(stmt, ctx);
}
stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(method, ctx));
ctx.decIndent();
ctx.println(`}`);
ctx.popClass();
return null;
}
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
if (field.hasModifier(o.StmtModifier.Final)) {
ctx.print(`final `);
} else if (isBlank(field.type)) {
ctx.print(`var `);
}
if (isPresent(field.type)) {
field.type.visitType(this, ctx);
ctx.print(` `);
}
ctx.println(`${field.name};`);
}
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
if (isPresent(getter.type)) {
getter.type.visitType(this, ctx);
ctx.print(` `);
}
ctx.println(`get ${getter.name} {`);
ctx.incIndent();
this.visitAllStatements(getter.body, ctx);
ctx.decIndent();
ctx.println(`}`);
}
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`${stmt.name}(`);
this._visitParams(stmt.constructorMethod.params, ctx);
ctx.print(`)`);
var ctorStmts = stmt.constructorMethod.body;
var superCtorExpr = ctorStmts.length > 0 ? getSuperConstructorCallExpr(ctorStmts[0]) : null;
if (isPresent(superCtorExpr)) {
ctx.print(`: `);
superCtorExpr.visitExpression(this, ctx);
ctorStmts = ctorStmts.slice(1);
}
ctx.println(` {`);
ctx.incIndent();
this.visitAllStatements(ctorStmts, ctx);
ctx.decIndent();
ctx.println(`}`);
}
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
if (isPresent(method.type)) {
method.type.visitType(this, ctx);
} else {
ctx.print(`void`);
}
ctx.print(` ${method.name}(`);
this._visitParams(method.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
this.visitAllStatements(method.body, ctx);
ctx.decIndent();
ctx.println(`}`);
}
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`);
this._visitParams(ast.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
this.visitAllStatements(ast.statements, ctx);
ctx.decIndent();
ctx.print(`}`);
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
if (isPresent(stmt.type)) {
stmt.type.visitType(this, ctx);
} else {
ctx.print(`void`);
}
ctx.print(` ${stmt.name}(`);
this._visitParams(stmt.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent();
ctx.println(`}`);
return null;
}
getBuiltinMethodName(method: o.BuiltinMethod): string {
var name;
switch (method) {
case o.BuiltinMethod.ConcatArray:
name = '.addAll';
break;
case o.BuiltinMethod.SubscribeObservable:
name = 'listen';
break;
case o.BuiltinMethod.bind:
name = null;
break;
default:
throw new BaseException(`Unknown builtin method: ${method}`);
}
return name;
}
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`);
ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}, ${CATCH_STACK_VAR.name}) {`);
ctx.incIndent();
this.visitAllStatements(stmt.catchStmts, ctx);
ctx.decIndent();
ctx.println(`}`);
return null;
}
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
switch (ast.operator) {
case o.BinaryOperator.Identical:
ctx.print(`identical(`);
ast.lhs.visitExpression(this, ctx);
ctx.print(`, `);
ast.rhs.visitExpression(this, ctx);
ctx.print(`)`);
break;
case o.BinaryOperator.NotIdentical:
ctx.print(`!identical(`);
ast.lhs.visitExpression(this, ctx);
ctx.print(`, `);
ast.rhs.visitExpression(this, ctx);
ctx.print(`)`);
break;
default:
super.visitBinaryOperatorExpr(ast, ctx);
}
return null;
}
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
if (isConstType(ast.type)) {
ctx.print(`const `);
}
return super.visitLiteralArrayExpr(ast, ctx);
}
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
if (isConstType(ast.type)) {
ctx.print(`const `);
}
if (isPresent(ast.valueType)) {
ctx.print(`<String, `);
ast.valueType.visitType(this, ctx);
ctx.print(`>`);
}
return super.visitLiteralMapExpr(ast, ctx);
}
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
ctx.print(isConstType(ast.type) ? `const` : `new`);
ctx.print(' ');
ast.classExpr.visitExpression(this, ctx);
ctx.print(`(`);
this.visitAllExpressions(ast.args, ctx, `,`);
ctx.print(`)`);
return null;
}
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
var typeStr;
switch (type.name) {
case o.BuiltinTypeName.Bool:
typeStr = 'bool';
break;
case o.BuiltinTypeName.Dynamic:
typeStr = 'dynamic';
break;
case o.BuiltinTypeName.Function:
typeStr = 'Function';
break;
case o.BuiltinTypeName.Number:
typeStr = 'num';
break;
case o.BuiltinTypeName.Int:
typeStr = 'int';
break;
case o.BuiltinTypeName.String:
typeStr = 'String';
break;
default:
throw new BaseException(`Unsupported builtin type ${type.name}`);
}
ctx.print(typeStr);
return null;
}
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;
}
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
ctx.print(`List<`);
if (isPresent(type.of)) {
type.of.visitType(this, ctx);
} else {
ctx.print(`dynamic`);
}
ctx.print(`>`);
return null;
}
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
ctx.print(`Map<String, `);
if (isPresent(type.valueType)) {
type.valueType.visitType(this, ctx);
} else {
ctx.print(`dynamic`);
}
ctx.print(`>`);
return null;
}
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects((param) => {
if (isPresent(param.type)) {
param.type.visitType(this, ctx);
ctx.print(' ');
}
ctx.print(param.name);
}, params, ctx, ',');
}
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
ctx: EmitterVisitorContext): void {
if (isBlank(value.name)) {
throw new BaseException(`Internal error: unknown identifier ${value}`);
}
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(value.moduleUrl, prefix);
}
ctx.print(`${prefix}.`);
}
ctx.print(value.name);
if (isPresent(typeParams) && typeParams.length > 0) {
ctx.print(`<`);
this.visitAllObjects((type) => type.visitType(this, ctx), typeParams, ctx, ',');
ctx.print(`>`);
}
}
}
function getSuperConstructorCallExpr(stmt: o.Statement): o.Expression {
if (stmt instanceof o.ExpressionStatement) {
var expr = stmt.expr;
if (expr instanceof o.InvokeFunctionExpr) {
var fn = expr.fn;
if (fn instanceof o.ReadVarExpr) {
if (fn.builtin === o.BuiltinVar.Super) {
return expr;
}
}
}
}
return null;
}
function isConstType(type: o.Type): boolean {
return isPresent(type) && type.hasModifier(o.TypeModifier.Const);
}

View File

@ -1,52 +0,0 @@
import {BaseException} from '../../src/facade/exceptions';
import {isPresent, isBlank, RegExpWrapper, Math} from '../../src/facade/lang';
import {Injectable} from '@angular/core';
import {AssetUrl, ImportGenerator} from './path_util';
var _PATH_SEP = '/';
var _PATH_SEP_RE = /\//g;
@Injectable()
export class DartImportGenerator implements ImportGenerator {
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
var moduleUrl = AssetUrl.parse(moduleUrlStr, false);
var importedUrl = AssetUrl.parse(importedUrlStr, true);
if (isBlank(importedUrl)) {
return importedUrlStr;
}
// Try to create a relative path first
if (moduleUrl.firstLevelDir == importedUrl.firstLevelDir &&
moduleUrl.packageName == importedUrl.packageName) {
return getRelativePath(moduleUrl.modulePath, importedUrl.modulePath);
} else if (importedUrl.firstLevelDir == 'lib') {
return `package:${importedUrl.packageName}/${importedUrl.modulePath}`;
}
throw new BaseException(`Can't import url ${importedUrlStr} from ${moduleUrlStr}`);
}
}
export function getRelativePath(modulePath: string, importedPath: string): string {
var moduleParts = modulePath.split(_PATH_SEP_RE);
var importedParts = importedPath.split(_PATH_SEP_RE);
var longestPrefix = getLongestPathSegmentPrefix(moduleParts, importedParts);
var resultParts = [];
var goParentCount = moduleParts.length - 1 - longestPrefix;
for (var i = 0; i < goParentCount; i++) {
resultParts.push('..');
}
for (var i = longestPrefix; i < importedParts.length; i++) {
resultParts.push(importedParts[i]);
}
return resultParts.join(_PATH_SEP);
}
export function getLongestPathSegmentPrefix(arr1: string[], arr2: string[]): number {
var prefixSize = 0;
var minLen = Math.min(arr1.length, arr2.length);
while (prefixSize < minLen && arr1[prefixSize] == arr2[prefixSize]) {
prefixSize++;
}
return prefixSize;
}

View File

@ -1,68 +0,0 @@
import {AppElement, AppView, DebugAppView} from '../../core_private';
import {isPresent} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {InstanceFactory, DynamicInstance} from './output_interpreter';
export class InterpretiveAppViewInstanceFactory implements InstanceFactory {
createInstance(superClass: any, clazz: any, args: any[], props: Map<string, any>,
getters: Map<string, Function>, methods: Map<string, Function>): any {
if (superClass === AppView) {
// We are always using DebugAppView as parent.
// However, in prod mode we generate a constructor call that does
// not have the argument for the debugNodeInfos.
args = args.concat([null]);
return new _InterpretiveAppView(args, props, getters, methods);
} else if (superClass === DebugAppView) {
return new _InterpretiveAppView(args, props, getters, methods);
}
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
}
}
class _InterpretiveAppView extends DebugAppView<any> implements DynamicInstance {
constructor(args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
public methods: Map<string, Function>) {
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
}
createInternal(rootSelector: string | any): AppElement {
var m = this.methods.get('createInternal');
if (isPresent(m)) {
return m(rootSelector);
} else {
return super.createInternal(rootSelector);
}
}
injectorGetInternal(token: any, nodeIndex: number, notFoundResult: any): any {
var m = this.methods.get('injectorGetInternal');
if (isPresent(m)) {
return m(token, nodeIndex, notFoundResult);
} else {
return super.injectorGet(token, nodeIndex, notFoundResult);
}
}
destroyInternal(): void {
var m = this.methods.get('destroyInternal');
if (isPresent(m)) {
return m();
} else {
return super.destroyInternal();
}
}
dirtyParentQueriesInternal(): void {
var m = this.methods.get('dirtyParentQueriesInternal');
if (isPresent(m)) {
return m();
} else {
return super.dirtyParentQueriesInternal();
}
}
detectChangesInternal(throwOnChange: boolean): void {
var m = this.methods.get('detectChangesInternal');
if (isPresent(m)) {
return m(throwOnChange);
} else {
return super.detectChangesInternal(throwOnChange);
}
}
}

View File

@ -1,78 +0,0 @@
import * as o from './output_ast';
import {
isPresent,
isBlank,
isString,
evalExpression,
RegExpWrapper,
StringWrapper
} from '../facade/lang';
import {BaseException} from '@angular/core';
import {OutputEmitter, EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import {ImportGenerator} from './path_util';
export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importGenerator: ImportGenerator) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
var converter = new JsEmitterVisitor(moduleUrl);
var ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
var srcParts = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`var ${prefix} = req` +
`uire('${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}');`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
}
}
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
importsWithPrefixes = new Map<string, string>();
constructor(private _moduleUrl: string) { super(); }
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
if (isBlank(ast.value.name)) {
throw new BaseException(`Internal error: unknown identifier ${ast.value}`);
}
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
var prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(ast.value.moduleUrl, prefix);
}
ctx.print(`${prefix}.`);
}
ctx.print(ast.value.name);
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareVarStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
}
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareFunctionStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
}
return null;
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareClassStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
}
return null;
}
}
function exportVar(varName: string): string {
return `Object.defineProperty(exports, '${varName}', { get: function() { return ${varName}; }});`;
}

View File

@ -1,869 +0,0 @@
import {isString, isPresent, isBlank} from '../../src/facade/lang';
import {CompileIdentifierMetadata} from '../compile_metadata';
//// Types
export enum TypeModifier {
Const
}
export abstract class Type {
constructor(public modifiers: TypeModifier[] = null) {
if (isBlank(modifiers)) {
this.modifiers = [];
}
}
abstract visitType(visitor: TypeVisitor, context: any): any;
hasModifier(modifier: TypeModifier): boolean { return this.modifiers.indexOf(modifier) !== -1; }
}
export enum BuiltinTypeName {
Dynamic,
Bool,
String,
Int,
Number,
Function
}
export class BuiltinType extends Type {
constructor(public name: BuiltinTypeName, modifiers: TypeModifier[] = null) { super(modifiers); }
visitType(visitor: TypeVisitor, context: any): any {
return visitor.visitBuiltintType(this, context);
}
}
export class ExternalType extends Type {
constructor(public value: CompileIdentifierMetadata, public typeParams: Type[] = null,
modifiers: TypeModifier[] = null) {
super(modifiers);
}
visitType(visitor: TypeVisitor, context: any): any {
return visitor.visitExternalType(this, context);
}
}
export class ArrayType extends Type {
constructor(public of: Type, modifiers: TypeModifier[] = null) { super(modifiers); }
visitType(visitor: TypeVisitor, context: any): any {
return visitor.visitArrayType(this, context);
}
}
export class MapType extends Type {
constructor(public valueType: Type, modifiers: TypeModifier[] = null) { super(modifiers); }
visitType(visitor: TypeVisitor, context: any): any { return visitor.visitMapType(this, context); }
}
export var DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
export var BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
export var INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
export var NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
export var STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
export var FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
export interface TypeVisitor {
visitBuiltintType(type: BuiltinType, context: any): any;
visitExternalType(type: ExternalType, context: any): any;
visitArrayType(type: ArrayType, context: any): any;
visitMapType(type: MapType, context: any): any;
}
///// Expressions
export enum BinaryOperator {
Equals,
NotEquals,
Identical,
NotIdentical,
Minus,
Plus,
Divide,
Multiply,
Modulo,
And,
Or,
Lower,
LowerEquals,
Bigger,
BiggerEquals
}
export abstract class Expression {
constructor(public type: Type) {}
abstract visitExpression(visitor: ExpressionVisitor, context: any): any;
prop(name: string): ReadPropExpr { return new ReadPropExpr(this, name); }
key(index: Expression, type: Type = null): ReadKeyExpr {
return new ReadKeyExpr(this, index, type);
}
callMethod(name: string | BuiltinMethod, params: Expression[]): InvokeMethodExpr {
return new InvokeMethodExpr(this, name, params);
}
callFn(params: Expression[]): InvokeFunctionExpr { return new InvokeFunctionExpr(this, params); }
instantiate(params: Expression[], type: Type = null): InstantiateExpr {
return new InstantiateExpr(this, params, type);
}
conditional(trueCase: Expression, falseCase: Expression = null): ConditionalExpr {
return new ConditionalExpr(this, trueCase, falseCase);
}
equals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs);
}
notEquals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs);
}
identical(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs);
}
notIdentical(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs);
}
minus(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs);
}
plus(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs);
}
divide(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs);
}
multiply(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs);
}
modulo(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs);
}
and(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs);
}
or(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs);
}
lower(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs);
}
lowerEquals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs);
}
bigger(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs);
}
biggerEquals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs);
}
isBlank(): Expression {
// Note: We use equals by purpose here to compare to null and undefined in JS.
return this.equals(NULL_EXPR);
}
cast(type: Type): Expression { return new CastExpr(this, type); }
toStmt(): Statement { return new ExpressionStatement(this); }
}
export enum BuiltinVar {
This,
Super,
CatchError,
CatchStack
}
export class ReadVarExpr extends Expression {
public name;
public builtin: BuiltinVar;
constructor(name: string | BuiltinVar, type: Type = null) {
super(type);
if (isString(name)) {
this.name = <string>name;
this.builtin = null;
} else {
this.name = null;
this.builtin = <BuiltinVar>name;
}
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadVarExpr(this, context);
}
set(value: Expression): WriteVarExpr { return new WriteVarExpr(this.name, value); }
}
export class WriteVarExpr extends Expression {
public value: Expression;
constructor(public name: string, value: Expression, type: Type = null) {
super(isPresent(type) ? type : value.type);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitWriteVarExpr(this, context);
}
toDeclStmt(type: Type = null, modifiers: StmtModifier[] = null): DeclareVarStmt {
return new DeclareVarStmt(this.name, this.value, type, modifiers);
}
}
export class WriteKeyExpr extends Expression {
public value: Expression;
constructor(public receiver: Expression, public index: Expression, value: Expression,
type: Type = null) {
super(isPresent(type) ? type : value.type);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitWriteKeyExpr(this, context);
}
}
export class WritePropExpr extends Expression {
public value: Expression;
constructor(public receiver: Expression, public name: string, value: Expression,
type: Type = null) {
super(isPresent(type) ? type : value.type);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitWritePropExpr(this, context);
}
}
export enum BuiltinMethod {
ConcatArray,
SubscribeObservable,
bind
}
export class InvokeMethodExpr extends Expression {
public name: string;
public builtin: BuiltinMethod;
constructor(public receiver: Expression, method: string | BuiltinMethod,
public args: Expression[], type: Type = null) {
super(type);
if (isString(method)) {
this.name = <string>method;
this.builtin = null;
} else {
this.name = null;
this.builtin = <BuiltinMethod>method;
}
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInvokeMethodExpr(this, context);
}
}
export class InvokeFunctionExpr extends Expression {
constructor(public fn: Expression, public args: Expression[], type: Type = null) { super(type); }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInvokeFunctionExpr(this, context);
}
}
export class InstantiateExpr extends Expression {
constructor(public classExpr: Expression, public args: Expression[], type?: Type) { super(type); }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInstantiateExpr(this, context);
}
}
export class LiteralExpr extends Expression {
constructor(public value: any, type: Type = null) { super(type); }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitLiteralExpr(this, context);
}
}
export class ExternalExpr extends Expression {
constructor(public value: CompileIdentifierMetadata, type: Type = null,
public typeParams: Type[] = null) {
super(type);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitExternalExpr(this, context);
}
}
export class ConditionalExpr extends Expression {
public trueCase: Expression;
constructor(public condition: Expression, trueCase: Expression,
public falseCase: Expression = null, type: Type = null) {
super(isPresent(type) ? type : trueCase.type);
this.trueCase = trueCase;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitConditionalExpr(this, context);
}
}
export class NotExpr extends Expression {
constructor(public condition: Expression) { super(BOOL_TYPE); }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitNotExpr(this, context);
}
}
export class CastExpr extends Expression {
constructor(public value: Expression, type: Type) { super(type); }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitCastExpr(this, context);
}
}
export class FnParam {
constructor(public name: string, public type: Type = null) {}
}
export class FunctionExpr extends Expression {
constructor(public params: FnParam[], public statements: Statement[], type: Type = null) {
super(type);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitFunctionExpr(this, context);
}
toDeclStmt(name: string, modifiers: StmtModifier[] = null): DeclareFunctionStmt {
return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers);
}
}
export class BinaryOperatorExpr extends Expression {
public lhs: Expression;
constructor(public operator: BinaryOperator, lhs: Expression, public rhs: Expression,
type: Type = null) {
super(isPresent(type) ? type : lhs.type);
this.lhs = lhs;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitBinaryOperatorExpr(this, context);
}
}
export class ReadPropExpr extends Expression {
constructor(public receiver: Expression, public name: string, type: Type = null) { super(type); }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadPropExpr(this, context);
}
set(value: Expression): WritePropExpr {
return new WritePropExpr(this.receiver, this.name, value);
}
}
export class ReadKeyExpr extends Expression {
constructor(public receiver: Expression, public index: Expression, type: Type = null) {
super(type);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadKeyExpr(this, context);
}
set(value: Expression): WriteKeyExpr {
return new WriteKeyExpr(this.receiver, this.index, value);
}
}
export class LiteralArrayExpr extends Expression {
public entries: Expression[];
constructor(entries: Expression[], type: Type = null) {
super(type);
this.entries = entries;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitLiteralArrayExpr(this, context);
}
}
export class LiteralMapExpr extends Expression {
public valueType: Type = null;
constructor(public entries: Array<Array<string | Expression>>, type: MapType = null) {
super(type);
if (isPresent(type)) {
this.valueType = type.valueType;
}
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitLiteralMapExpr(this, context);
}
}
export interface ExpressionVisitor {
visitReadVarExpr(ast: ReadVarExpr, context: any): any;
visitWriteVarExpr(expr: WriteVarExpr, context: any): any;
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any;
visitWritePropExpr(expr: WritePropExpr, context: any): any;
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any;
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any;
visitInstantiateExpr(ast: InstantiateExpr, context: any): any;
visitLiteralExpr(ast: LiteralExpr, context: any): any;
visitExternalExpr(ast: ExternalExpr, context: any): any;
visitConditionalExpr(ast: ConditionalExpr, context: any): any;
visitNotExpr(ast: NotExpr, context: any): any;
visitCastExpr(ast: CastExpr, context: any): any;
visitFunctionExpr(ast: FunctionExpr, context: any): any;
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any;
visitReadPropExpr(ast: ReadPropExpr, context: any): any;
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any;
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any;
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any;
}
export var THIS_EXPR = new ReadVarExpr(BuiltinVar.This);
export var SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super);
export var CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError);
export var CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack);
export var NULL_EXPR = new LiteralExpr(null, null);
//// Statements
export enum StmtModifier {
Final,
Private
}
export abstract class Statement {
constructor(public modifiers: StmtModifier[] = null) {
if (isBlank(modifiers)) {
this.modifiers = [];
}
}
abstract visitStatement(visitor: StatementVisitor, context: any): any;
hasModifier(modifier: StmtModifier): boolean { return this.modifiers.indexOf(modifier) !== -1; }
}
export class DeclareVarStmt extends Statement {
public type: Type;
constructor(public name: string, public value: Expression, type: Type = null,
modifiers: StmtModifier[] = null) {
super(modifiers);
this.type = isPresent(type) ? type : value.type;
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitDeclareVarStmt(this, context);
}
}
export class DeclareFunctionStmt extends Statement {
constructor(public name: string, public params: FnParam[], public statements: Statement[],
public type: Type = null, modifiers: StmtModifier[] = null) {
super(modifiers);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitDeclareFunctionStmt(this, context);
}
}
export class ExpressionStatement extends Statement {
constructor(public expr: Expression) { super(); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitExpressionStmt(this, context);
}
}
export class ReturnStatement extends Statement {
constructor(public value: Expression) { super(); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitReturnStmt(this, context);
}
}
export class AbstractClassPart {
constructor(public type: Type = null, public modifiers: StmtModifier[]) {
if (isBlank(modifiers)) {
this.modifiers = [];
}
}
hasModifier(modifier: StmtModifier): boolean { return this.modifiers.indexOf(modifier) !== -1; }
}
export class ClassField extends AbstractClassPart {
constructor(public name: string, type: Type = null, modifiers: StmtModifier[] = null) {
super(type, modifiers);
}
}
export class ClassMethod extends AbstractClassPart {
constructor(public name: string, public params: FnParam[], public body: Statement[],
type: Type = null, modifiers: StmtModifier[] = null) {
super(type, modifiers);
}
}
export class ClassGetter extends AbstractClassPart {
constructor(public name: string, public body: Statement[], type: Type = null,
modifiers: StmtModifier[] = null) {
super(type, modifiers);
}
}
export class ClassStmt extends Statement {
constructor(public name: string, public parent: Expression, public fields: ClassField[],
public getters: ClassGetter[], public constructorMethod: ClassMethod,
public methods: ClassMethod[], modifiers: StmtModifier[] = null) {
super(modifiers);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitDeclareClassStmt(this, context);
}
}
export class IfStmt extends Statement {
constructor(public condition: Expression, public trueCase: Statement[],
public falseCase: Statement[] = /*@ts2dart_const*/[]) {
super();
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitIfStmt(this, context);
}
}
export class CommentStmt extends Statement {
constructor(public comment: string) { super(); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitCommentStmt(this, context);
}
}
export class TryCatchStmt extends Statement {
constructor(public bodyStmts: Statement[], public catchStmts: Statement[]) { super(); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitTryCatchStmt(this, context);
}
}
export class ThrowStmt extends Statement {
constructor(public error: Expression) { super(); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitThrowStmt(this, context);
}
}
export interface StatementVisitor {
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any;
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any;
visitExpressionStmt(stmt: ExpressionStatement, context: any): any;
visitReturnStmt(stmt: ReturnStatement, context: any): any;
visitDeclareClassStmt(stmt: ClassStmt, context: any): any;
visitIfStmt(stmt: IfStmt, context: any): any;
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any;
visitThrowStmt(stmt: ThrowStmt, context: any): any;
visitCommentStmt(stmt: CommentStmt, context: any): any;
}
export class ExpressionTransformer implements StatementVisitor, ExpressionVisitor {
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; }
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
return new WriteVarExpr(expr.name, expr.value.visitExpression(this, context));
}
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any {
return new WriteKeyExpr(expr.receiver.visitExpression(this, context),
expr.index.visitExpression(this, context),
expr.value.visitExpression(this, context));
}
visitWritePropExpr(expr: WritePropExpr, context: any): any {
return new WritePropExpr(expr.receiver.visitExpression(this, context), expr.name,
expr.value.visitExpression(this, context));
}
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
var method = isPresent(ast.builtin) ? ast.builtin : ast.name;
return new InvokeMethodExpr(ast.receiver.visitExpression(this, context), method,
this.visitAllExpressions(ast.args, context), ast.type);
}
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any {
return new InvokeFunctionExpr(ast.fn.visitExpression(this, context),
this.visitAllExpressions(ast.args, context), ast.type);
}
visitInstantiateExpr(ast: InstantiateExpr, context: any): any {
return new InstantiateExpr(ast.classExpr.visitExpression(this, context),
this.visitAllExpressions(ast.args, context), ast.type);
}
visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; }
visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; }
visitConditionalExpr(ast: ConditionalExpr, context: any): any {
return new ConditionalExpr(ast.condition.visitExpression(this, context),
ast.trueCase.visitExpression(this, context),
ast.falseCase.visitExpression(this, context));
}
visitNotExpr(ast: NotExpr, context: any): any {
return new NotExpr(ast.condition.visitExpression(this, context));
}
visitCastExpr(ast: CastExpr, context: any): any {
return new CastExpr(ast.value.visitExpression(this, context), context);
}
visitFunctionExpr(ast: FunctionExpr, context: any): any {
// Don't descend into nested functions
return ast;
}
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any {
return new BinaryOperatorExpr(ast.operator, ast.lhs.visitExpression(this, context),
ast.rhs.visitExpression(this, context), ast.type);
}
visitReadPropExpr(ast: ReadPropExpr, context: any): any {
return new ReadPropExpr(ast.receiver.visitExpression(this, context), ast.name, ast.type);
}
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any {
return new ReadKeyExpr(ast.receiver.visitExpression(this, context),
ast.index.visitExpression(this, context), ast.type);
}
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
}
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
return new LiteralMapExpr(ast.entries.map(
(entry) => [entry[0], (<Expression>entry[1]).visitExpression(this, context)]));
}
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
return exprs.map(expr => expr.visitExpression(this, context));
}
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
return new DeclareVarStmt(stmt.name, stmt.value.visitExpression(this, context), stmt.type,
stmt.modifiers);
}
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
// Don't descend into nested functions
return stmt;
}
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
return new ExpressionStatement(stmt.expr.visitExpression(this, context));
}
visitReturnStmt(stmt: ReturnStatement, context: any): any {
return new ReturnStatement(stmt.value.visitExpression(this, context));
}
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
// Don't descend into nested functions
return stmt;
}
visitIfStmt(stmt: IfStmt, context: any): any {
return new IfStmt(stmt.condition.visitExpression(this, context),
this.visitAllStatements(stmt.trueCase, context),
this.visitAllStatements(stmt.falseCase, context));
}
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any {
return new TryCatchStmt(this.visitAllStatements(stmt.bodyStmts, context),
this.visitAllStatements(stmt.catchStmts, context));
}
visitThrowStmt(stmt: ThrowStmt, context: any): any {
return new ThrowStmt(stmt.error.visitExpression(this, context));
}
visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; }
visitAllStatements(stmts: Statement[], context: any): Statement[] {
return stmts.map(stmt => stmt.visitStatement(this, context));
}
}
export class RecursiveExpressionVisitor implements StatementVisitor, ExpressionVisitor {
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; }
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
expr.value.visitExpression(this, context);
return expr;
}
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any {
expr.receiver.visitExpression(this, context);
expr.index.visitExpression(this, context);
expr.value.visitExpression(this, context);
return expr;
}
visitWritePropExpr(expr: WritePropExpr, context: any): any {
expr.receiver.visitExpression(this, context);
expr.value.visitExpression(this, context);
return expr;
}
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
ast.receiver.visitExpression(this, context);
this.visitAllExpressions(ast.args, context);
return ast;
}
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any {
ast.fn.visitExpression(this, context);
this.visitAllExpressions(ast.args, context);
return ast;
}
visitInstantiateExpr(ast: InstantiateExpr, context: any): any {
ast.classExpr.visitExpression(this, context);
this.visitAllExpressions(ast.args, context);
return ast;
}
visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; }
visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; }
visitConditionalExpr(ast: ConditionalExpr, context: any): any {
ast.condition.visitExpression(this, context);
ast.trueCase.visitExpression(this, context);
ast.falseCase.visitExpression(this, context);
return ast;
}
visitNotExpr(ast: NotExpr, context: any): any {
ast.condition.visitExpression(this, context);
return ast;
}
visitCastExpr(ast: CastExpr, context: any): any {
ast.value.visitExpression(this, context);
return ast;
}
visitFunctionExpr(ast: FunctionExpr, context: any): any { return ast; }
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any {
ast.lhs.visitExpression(this, context);
ast.rhs.visitExpression(this, context);
return ast;
}
visitReadPropExpr(ast: ReadPropExpr, context: any): any {
ast.receiver.visitExpression(this, context);
return ast;
}
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any {
ast.receiver.visitExpression(this, context);
ast.index.visitExpression(this, context);
return ast;
}
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
this.visitAllExpressions(ast.entries, context);
return ast;
}
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
ast.entries.forEach((entry) => (<Expression>entry[1]).visitExpression(this, context));
return ast;
}
visitAllExpressions(exprs: Expression[], context: any): void {
exprs.forEach(expr => expr.visitExpression(this, context));
}
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
stmt.value.visitExpression(this, context);
return stmt;
}
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
// Don't descend into nested functions
return stmt;
}
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
stmt.expr.visitExpression(this, context);
return stmt;
}
visitReturnStmt(stmt: ReturnStatement, context: any): any {
stmt.value.visitExpression(this, context);
return stmt;
}
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
// Don't descend into nested functions
return stmt;
}
visitIfStmt(stmt: IfStmt, context: any): any {
stmt.condition.visitExpression(this, context);
this.visitAllStatements(stmt.trueCase, context);
this.visitAllStatements(stmt.falseCase, context);
return stmt;
}
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any {
this.visitAllStatements(stmt.bodyStmts, context);
this.visitAllStatements(stmt.catchStmts, context);
return stmt;
}
visitThrowStmt(stmt: ThrowStmt, context: any): any {
stmt.error.visitExpression(this, context);
return stmt;
}
visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; }
visitAllStatements(stmts: Statement[], context: any): void {
stmts.forEach(stmt => stmt.visitStatement(this, context));
}
}
export function replaceVarInExpression(varName: string, newValue: Expression,
expression: Expression): Expression {
var transformer = new _ReplaceVariableTransformer(varName, newValue);
return expression.visitExpression(transformer, null);
}
class _ReplaceVariableTransformer extends ExpressionTransformer {
constructor(private _varName: string, private _newValue: Expression) { super(); }
visitReadVarExpr(ast: ReadVarExpr, context: any): any {
return ast.name == this._varName ? this._newValue : ast;
}
}
export function findReadVarNames(stmts: Statement[]): Set<string> {
var finder = new _VariableFinder();
finder.visitAllStatements(stmts, null);
return finder.varNames;
}
class _VariableFinder extends RecursiveExpressionVisitor {
varNames = new Set<string>();
visitReadVarExpr(ast: ReadVarExpr, context: any): any {
this.varNames.add(ast.name);
return null;
}
}
export function variable(name: string, type: Type = null): ReadVarExpr {
return new ReadVarExpr(name, type);
}
export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = null): ExternalExpr {
return new ExternalExpr(id, null, typeParams);
}
export function importType(id: CompileIdentifierMetadata, typeParams: Type[] = null,
typeModifiers: TypeModifier[] = null): ExternalType {
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null;
}
export function literal(value: any, type: Type = null): LiteralExpr {
return new LiteralExpr(value, type);
}
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {
return new LiteralArrayExpr(values, type);
}
export function literalMap(values: Array<Array<string | Expression>>,
type: MapType = null): LiteralMapExpr {
return new LiteralMapExpr(values, type);
}
export function not(expr: Expression): NotExpr {
return new NotExpr(expr);
}
export function fn(params: FnParam[], body: Statement[], type: Type = null): FunctionExpr {
return new FunctionExpr(params, body, type);
}

View File

@ -1,416 +0,0 @@
import {reflector} from '@angular/core';
import {isPresent, IS_DART, FunctionWrapper} from '../../src/facade/lang';
import {ObservableWrapper} from '../../src/facade/async';
import {BaseException, unimplemented} from '../../src/facade/exceptions';
import {ListWrapper} from '../../src/facade/collection';
import * as o from './output_ast';
import {debugOutputAstAsDart} from './dart_emitter';
import {debugOutputAstAsTypeScript} from './ts_emitter';
export function interpretStatements(statements: o.Statement[], resultVar: string,
instanceFactory: InstanceFactory): any {
var stmtsWithReturn = statements.concat([new o.ReturnStatement(o.variable(resultVar))]);
var ctx = new _ExecutionContext(null, null, null, null, new Map<string, any>(),
new Map<string, any>(), new Map<string, Function>(),
new Map<string, Function>(), instanceFactory);
var visitor = new StatementInterpreter();
var result = visitor.visitAllStatements(stmtsWithReturn, ctx);
return isPresent(result) ? result.value : null;
}
export interface InstanceFactory {
createInstance(superClass: any, clazz: any, constructorArgs: any[], props: Map<string, any>,
getters: Map<string, Function>, methods: Map<string, Function>): DynamicInstance;
}
export abstract class DynamicInstance {
get props(): Map<string, any> { return unimplemented(); }
get getters(): Map<string, Function> { return unimplemented(); }
get methods(): Map<string, any> { return unimplemented(); }
get clazz(): any { return unimplemented(); }
}
function isDynamicInstance(instance: any): any {
if (IS_DART) {
return instance instanceof DynamicInstance;
} else {
return isPresent(instance) && isPresent(instance.props) && isPresent(instance.getters) &&
isPresent(instance.methods);
}
}
function _executeFunctionStatements(varNames: string[], varValues: any[], statements: o.Statement[],
ctx: _ExecutionContext, visitor: StatementInterpreter): any {
var childCtx = ctx.createChildWihtLocalVars();
for (var i = 0; i < varNames.length; i++) {
childCtx.vars.set(varNames[i], varValues[i]);
}
var result = visitor.visitAllStatements(statements, childCtx);
return isPresent(result) ? result.value : null;
}
class _ExecutionContext {
constructor(public parent: _ExecutionContext, public superClass: any, public superInstance: any,
public className: string, public vars: Map<string, any>,
public props: Map<string, any>, public getters: Map<string, Function>,
public methods: Map<string, Function>, public instanceFactory: InstanceFactory) {}
createChildWihtLocalVars(): _ExecutionContext {
return new _ExecutionContext(this, this.superClass, this.superInstance, this.className,
new Map<string, any>(), this.props, this.getters, this.methods,
this.instanceFactory);
}
}
class ReturnValue {
constructor(public value: any) {}
}
class _DynamicClass {
constructor(private _classStmt: o.ClassStmt, private _ctx: _ExecutionContext,
private _visitor: StatementInterpreter) {}
instantiate(args: any[]): DynamicInstance {
var props = new Map<string, any>();
var getters = new Map<string, Function>();
var methods = new Map<string, Function>();
var superClass = this._classStmt.parent.visitExpression(this._visitor, this._ctx);
var instanceCtx =
new _ExecutionContext(this._ctx, superClass, null, this._classStmt.name, this._ctx.vars,
props, getters, methods, this._ctx.instanceFactory);
this._classStmt.fields.forEach((field: o.ClassField) => { props.set(field.name, null); });
this._classStmt.getters.forEach((getter: o.ClassGetter) => {
getters.set(getter.name, () => _executeFunctionStatements([], [], getter.body, instanceCtx,
this._visitor));
});
this._classStmt.methods.forEach((method: o.ClassMethod) => {
var paramNames = method.params.map(param => param.name);
methods.set(method.name, _declareFn(paramNames, method.body, instanceCtx, this._visitor));
});
var ctorParamNames = this._classStmt.constructorMethod.params.map(param => param.name);
_executeFunctionStatements(ctorParamNames, args, this._classStmt.constructorMethod.body,
instanceCtx, this._visitor);
return instanceCtx.superInstance;
}
debugAst(): string { return this._visitor.debugAst(this._classStmt); }
}
class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
debugAst(ast: o.Expression | o.Statement | o.Type): string {
return IS_DART ? debugOutputAstAsDart(ast) : debugOutputAstAsTypeScript(ast);
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: _ExecutionContext): any {
ctx.vars.set(stmt.name, stmt.value.visitExpression(this, ctx));
return null;
}
visitWriteVarExpr(expr: o.WriteVarExpr, ctx: _ExecutionContext): any {
var value = expr.value.visitExpression(this, ctx);
var currCtx = ctx;
while (currCtx != null) {
if (currCtx.vars.has(expr.name)) {
currCtx.vars.set(expr.name, value);
return value;
}
currCtx = currCtx.parent;
}
throw new BaseException(`Not declared variable ${expr.name}`);
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: _ExecutionContext): any {
var varName = ast.name;
if (isPresent(ast.builtin)) {
switch (ast.builtin) {
case o.BuiltinVar.Super:
case o.BuiltinVar.This:
return ctx.superInstance;
case o.BuiltinVar.CatchError:
varName = CATCH_ERROR_VAR;
break;
case o.BuiltinVar.CatchStack:
varName = CATCH_STACK_VAR;
break;
default:
throw new BaseException(`Unknown builtin variable ${ast.builtin}`);
}
}
var currCtx = ctx;
while (currCtx != null) {
if (currCtx.vars.has(varName)) {
return currCtx.vars.get(varName);
}
currCtx = currCtx.parent;
}
throw new BaseException(`Not declared variable ${varName}`);
}
visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: _ExecutionContext): any {
var receiver = expr.receiver.visitExpression(this, ctx);
var index = expr.index.visitExpression(this, ctx);
var value = expr.value.visitExpression(this, ctx);
receiver[index] = value;
return value;
}
visitWritePropExpr(expr: o.WritePropExpr, ctx: _ExecutionContext): any {
var receiver = expr.receiver.visitExpression(this, ctx);
var value = expr.value.visitExpression(this, ctx);
if (isDynamicInstance(receiver)) {
var di = <DynamicInstance>receiver;
if (di.props.has(expr.name)) {
di.props.set(expr.name, value);
} else {
reflector.setter(expr.name)(receiver, value);
}
} else {
reflector.setter(expr.name)(receiver, value);
}
return value;
}
visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: _ExecutionContext): any {
var receiver = expr.receiver.visitExpression(this, ctx);
var args = this.visitAllExpressions(expr.args, ctx);
var result;
if (isPresent(expr.builtin)) {
switch (expr.builtin) {
case o.BuiltinMethod.ConcatArray:
result = ListWrapper.concat(receiver, args[0]);
break;
case o.BuiltinMethod.SubscribeObservable:
result = ObservableWrapper.subscribe(receiver, args[0]);
break;
case o.BuiltinMethod.bind:
if (IS_DART) {
result = receiver;
} else {
result = receiver.bind(args[0]);
}
break;
default:
throw new BaseException(`Unknown builtin method ${expr.builtin}`);
}
} else if (isDynamicInstance(receiver)) {
var di = <DynamicInstance>receiver;
if (di.methods.has(expr.name)) {
result = FunctionWrapper.apply(di.methods.get(expr.name), args);
} else {
result = reflector.method(expr.name)(receiver, args);
}
} else {
result = reflector.method(expr.name)(receiver, args);
}
return result;
}
visitInvokeFunctionExpr(stmt: o.InvokeFunctionExpr, ctx: _ExecutionContext): any {
var args = this.visitAllExpressions(stmt.args, ctx);
var fnExpr = stmt.fn;
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
ctx.superInstance = ctx.instanceFactory.createInstance(ctx.superClass, ctx.className, args,
ctx.props, ctx.getters, ctx.methods);
ctx.parent.superInstance = ctx.superInstance;
return null;
} else {
var fn = stmt.fn.visitExpression(this, ctx);
return FunctionWrapper.apply(fn, args);
}
}
visitReturnStmt(stmt: o.ReturnStatement, ctx: _ExecutionContext): any {
return new ReturnValue(stmt.value.visitExpression(this, ctx));
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: _ExecutionContext): any {
var clazz = new _DynamicClass(stmt, ctx, this);
ctx.vars.set(stmt.name, clazz);
return null;
}
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: _ExecutionContext): any {
return stmt.expr.visitExpression(this, ctx);
}
visitIfStmt(stmt: o.IfStmt, ctx: _ExecutionContext): any {
var condition = stmt.condition.visitExpression(this, ctx);
if (condition) {
return this.visitAllStatements(stmt.trueCase, ctx);
} else if (isPresent(stmt.falseCase)) {
return this.visitAllStatements(stmt.falseCase, ctx);
}
return null;
}
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: _ExecutionContext): any {
try {
return this.visitAllStatements(stmt.bodyStmts, ctx);
} catch (e) {
var childCtx = ctx.createChildWihtLocalVars();
childCtx.vars.set(CATCH_ERROR_VAR, e);
childCtx.vars.set(CATCH_STACK_VAR, e.stack);
return this.visitAllStatements(stmt.catchStmts, childCtx);
}
}
visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any {
throw stmt.error.visitExpression(this, ctx);
}
visitCommentStmt(stmt: o.CommentStmt, context?: any): any { return null; }
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any {
var args = this.visitAllExpressions(ast.args, ctx);
var clazz = ast.classExpr.visitExpression(this, ctx);
if (clazz instanceof _DynamicClass) {
return clazz.instantiate(args);
} else {
return FunctionWrapper.apply(reflector.factory(clazz), args);
}
}
visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { return ast.value; }
visitExternalExpr(ast: o.ExternalExpr, ctx: _ExecutionContext): any { return ast.value.runtime; }
visitConditionalExpr(ast: o.ConditionalExpr, ctx: _ExecutionContext): any {
if (ast.condition.visitExpression(this, ctx)) {
return ast.trueCase.visitExpression(this, ctx);
} else if (isPresent(ast.falseCase)) {
return ast.falseCase.visitExpression(this, ctx);
}
return null;
}
visitNotExpr(ast: o.NotExpr, ctx: _ExecutionContext): any {
return !ast.condition.visitExpression(this, ctx);
}
visitCastExpr(ast: o.CastExpr, ctx: _ExecutionContext): any {
return ast.value.visitExpression(this, ctx);
}
visitFunctionExpr(ast: o.FunctionExpr, ctx: _ExecutionContext): any {
var paramNames = ast.params.map((param) => param.name);
return _declareFn(paramNames, ast.statements, ctx, this);
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: _ExecutionContext): any {
var paramNames = stmt.params.map((param) => param.name);
ctx.vars.set(stmt.name, _declareFn(paramNames, stmt.statements, ctx, this));
return null;
}
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: _ExecutionContext): any {
var lhs = () => ast.lhs.visitExpression(this, ctx);
var rhs = () => ast.rhs.visitExpression(this, ctx);
switch (ast.operator) {
case o.BinaryOperator.Equals:
return lhs() == rhs();
case o.BinaryOperator.Identical:
return lhs() === rhs();
case o.BinaryOperator.NotEquals:
return lhs() != rhs();
case o.BinaryOperator.NotIdentical:
return lhs() !== rhs();
case o.BinaryOperator.And:
return lhs() && rhs();
case o.BinaryOperator.Or:
return lhs() || rhs();
case o.BinaryOperator.Plus:
return lhs() + rhs();
case o.BinaryOperator.Minus:
return lhs() - rhs();
case o.BinaryOperator.Divide:
return lhs() / rhs();
case o.BinaryOperator.Multiply:
return lhs() * rhs();
case o.BinaryOperator.Modulo:
return lhs() % rhs();
case o.BinaryOperator.Lower:
return lhs() < rhs();
case o.BinaryOperator.LowerEquals:
return lhs() <= rhs();
case o.BinaryOperator.Bigger:
return lhs() > rhs();
case o.BinaryOperator.BiggerEquals:
return lhs() >= rhs();
default:
throw new BaseException(`Unknown operator ${ast.operator}`);
}
}
visitReadPropExpr(ast: o.ReadPropExpr, ctx: _ExecutionContext): any {
var result;
var receiver = ast.receiver.visitExpression(this, ctx);
if (isDynamicInstance(receiver)) {
var di = <DynamicInstance>receiver;
if (di.props.has(ast.name)) {
result = di.props.get(ast.name);
} else if (di.getters.has(ast.name)) {
result = di.getters.get(ast.name)();
} else if (di.methods.has(ast.name)) {
result = di.methods.get(ast.name);
} else {
result = reflector.getter(ast.name)(receiver);
}
} else {
result = reflector.getter(ast.name)(receiver);
}
return result;
}
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: _ExecutionContext): any {
var receiver = ast.receiver.visitExpression(this, ctx);
var prop = ast.index.visitExpression(this, ctx);
return receiver[prop];
}
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: _ExecutionContext): any {
return this.visitAllExpressions(ast.entries, ctx);
}
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: _ExecutionContext): any {
var result = {};
ast.entries.forEach((entry) => result[<string>entry[0]] =
(<o.Expression>entry[1]).visitExpression(this, ctx));
return result;
}
visitAllExpressions(expressions: o.Expression[], ctx: _ExecutionContext): any {
return expressions.map((expr) => expr.visitExpression(this, ctx));
}
visitAllStatements(statements: o.Statement[], ctx: _ExecutionContext): ReturnValue {
for (var i = 0; i < statements.length; i++) {
var stmt = statements[i];
var val = stmt.visitStatement(this, ctx);
if (val instanceof ReturnValue) {
return val;
}
}
return null;
}
}
function _declareFn(varNames: string[], statements: o.Statement[], ctx: _ExecutionContext,
visitor: StatementInterpreter): Function {
switch (varNames.length) {
case 0:
return () => _executeFunctionStatements(varNames, [], statements, ctx, visitor);
case 1:
return (d0) => _executeFunctionStatements(varNames, [d0], statements, ctx, visitor);
case 2:
return (d0, d1) => _executeFunctionStatements(varNames, [d0, d1], statements, ctx, visitor);
case 3:
return (d0, d1, d2) =>
_executeFunctionStatements(varNames, [d0, d1, d2], statements, ctx, visitor);
case 4:
return (d0, d1, d2, d3) =>
_executeFunctionStatements(varNames, [d0, d1, d2, d3], statements, ctx, visitor);
case 5:
return (d0, d1, d2, d3, d4) => _executeFunctionStatements(varNames, [d0, d1, d2, d3, d4],
statements, ctx, visitor);
case 6:
return (d0, d1, d2, d3, d4, d5) => _executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5], statements, ctx, visitor);
case 7:
return (d0, d1, d2, d3, d4, d5, d6) => _executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6], statements, ctx, visitor);
case 8:
return (d0, d1, d2, d3, d4, d5, d6, d7) => _executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6, d7], statements, ctx, visitor);
case 9:
return (d0, d1, d2, d3, d4, d5, d6, d7, d8) => _executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6, d7, d8], statements, ctx, visitor);
case 10:
return (d0, d1, d2, d3, d4, d5, d6, d7, d8, d9) => _executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9], statements, ctx, visitor);
default:
throw new BaseException(
'Declaring functions with more than 10 arguments is not supported right now');
}
}
var CATCH_ERROR_VAR = 'error';
var CATCH_STACK_VAR = 'stack';

View File

@ -1,42 +0,0 @@
import {
isPresent,
evalExpression,
} from '../../src/facade/lang';
import * as o from './output_ast';
import {EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import {sanitizeIdentifier} from '../util';
export function jitStatements(sourceUrl: string, statements: o.Statement[],
resultVar: string): any {
var converter = new JitEmitterVisitor();
var ctx = EmitterVisitorContext.createRoot([resultVar]);
converter.visitAllStatements(statements, ctx);
return evalExpression(sourceUrl, resultVar, ctx.toSource(), converter.getArgs());
}
class JitEmitterVisitor extends AbstractJsEmitterVisitor {
private _evalArgNames: string[] = [];
private _evalArgValues: any[] = [];
getArgs(): {[key: string]: any} {
var result = {};
for (var i = 0; i < this._evalArgNames.length; i++) {
result[this._evalArgNames[i]] = this._evalArgValues[i];
}
return result;
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
var value = ast.value.runtime;
var id = this._evalArgValues.indexOf(value);
if (id === -1) {
id = this._evalArgValues.length;
this._evalArgValues.push(value);
var name = isPresent(ast.value.name) ? sanitizeIdentifier(ast.value.name) : 'val';
this._evalArgNames.push(sanitizeIdentifier(`jit_${name}${id}`));
}
ctx.print(this._evalArgNames[id]);
return null;
}
}

View File

@ -1,31 +0,0 @@
import {BaseException} from '../../src/facade/exceptions';
import {isPresent, isBlank, RegExpWrapper, Math} from '../../src/facade/lang';
import {Injectable} from '@angular/core';
// asset:<package-name>/<realm>/<path-to-module>
var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/g;
/**
* Interface that defines how import statements should be generated.
*/
export abstract class ImportGenerator {
static parseAssetUrl(url: string): AssetUrl { return AssetUrl.parse(url); }
abstract getImportPath(moduleUrlStr: string, importedUrlStr: string): string;
}
export class AssetUrl {
static parse(url: string, allowNonMatching: boolean = true): AssetUrl {
var match = RegExpWrapper.firstMatch(_ASSET_URL_RE, url);
if (isPresent(match)) {
return new AssetUrl(match[1], match[2], match[3]);
}
if (allowNonMatching) {
return null;
}
throw new BaseException(`Url ${url} is not a valid asset: url`);
}
constructor(public packageName: string, public firstLevelDir: string, public modulePath: string) {
}
}

View File

@ -1,324 +0,0 @@
import * as o from './output_ast';
import {isPresent, isBlank, isArray} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {
OutputEmitter,
EmitterVisitorContext,
AbstractEmitterVisitor,
CATCH_ERROR_VAR,
CATCH_STACK_VAR
} from './abstract_emitter';
import {ImportGenerator} from './path_util';
var _debugModuleUrl = 'asset://debug/lib';
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type |
any[]): string {
var converter = new _TsEmitterVisitor(_debugModuleUrl);
var ctx = EmitterVisitorContext.createRoot([]);
var asts: any[];
if (isArray(ast)) {
asts = <any[]>ast;
} else {
asts = [ast];
}
asts.forEach((ast) => {
if (ast instanceof o.Statement) {
ast.visitStatement(converter, ctx);
} else if (ast instanceof o.Expression) {
ast.visitExpression(converter, ctx);
} else if (ast instanceof o.Type) {
ast.visitType(converter, ctx);
} else {
throw new BaseException(`Don't know how to print debug info for ${ast}`);
}
});
return ctx.toSource();
}
export class TypeScriptEmitter implements OutputEmitter {
constructor(private _importGenerator: ImportGenerator) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
var converter = new _TsEmitterVisitor(moduleUrl);
var ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
var srcParts = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`imp` +
`ort * as ${prefix} from '${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}';`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
}
}
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
constructor(private _moduleUrl: string) { super(false); }
importsWithPrefixes = new Map<string, string>();
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
}
if (stmt.hasModifier(o.StmtModifier.Final)) {
ctx.print(`const`);
} else {
ctx.print(`var`);
}
ctx.print(` ${stmt.name}`);
if (isPresent(stmt.type)) {
ctx.print(`:`);
stmt.type.visitType(this, ctx);
}
ctx.print(` = `);
stmt.value.visitExpression(this, ctx);
ctx.println(`;`);
return null;
}
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(<`);
ast.type.visitType(this, ctx);
ctx.print(`>`);
ast.value.visitExpression(this, ctx);
ctx.print(`)`);
return null;
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
ctx.pushClass(stmt);
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
}
ctx.print(`class ${stmt.name}`);
if (isPresent(stmt.parent)) {
ctx.print(` extends `);
stmt.parent.visitExpression(this, ctx);
}
ctx.println(` {`);
ctx.incIndent();
stmt.fields.forEach((field) => this._visitClassField(field, ctx));
if (isPresent(stmt.constructorMethod)) {
this._visitClassConstructor(stmt, ctx);
}
stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(method, ctx));
ctx.decIndent();
ctx.println(`}`);
ctx.popClass();
return null;
}
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
if (field.hasModifier(o.StmtModifier.Private)) {
ctx.print(`private `);
}
ctx.print(field.name);
if (isPresent(field.type)) {
ctx.print(`:`);
field.type.visitType(this, ctx);
} else {
ctx.print(`: any`);
}
ctx.println(`;`);
}
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
if (getter.hasModifier(o.StmtModifier.Private)) {
ctx.print(`private `);
}
ctx.print(`get ${getter.name}()`);
if (isPresent(getter.type)) {
ctx.print(`:`);
getter.type.visitType(this, ctx);
}
ctx.println(` {`);
ctx.incIndent();
this.visitAllStatements(getter.body, ctx);
ctx.decIndent();
ctx.println(`}`);
}
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`constructor(`);
this._visitParams(stmt.constructorMethod.params, ctx);
ctx.println(`) {`);
ctx.incIndent();
this.visitAllStatements(stmt.constructorMethod.body, ctx);
ctx.decIndent();
ctx.println(`}`);
}
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
if (method.hasModifier(o.StmtModifier.Private)) {
ctx.print(`private `);
}
ctx.print(`${method.name}(`);
this._visitParams(method.params, ctx);
ctx.print(`):`);
if (isPresent(method.type)) {
method.type.visitType(this, ctx);
} else {
ctx.print(`void`);
}
ctx.println(` {`);
ctx.incIndent();
this.visitAllStatements(method.body, ctx);
ctx.decIndent();
ctx.println(`}`);
}
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`);
this._visitParams(ast.params, ctx);
ctx.print(`):`);
if (isPresent(ast.type)) {
ast.type.visitType(this, ctx);
} else {
ctx.print(`void`);
}
ctx.println(` => {`);
ctx.incIndent();
this.visitAllStatements(ast.statements, ctx);
ctx.decIndent();
ctx.print(`}`);
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
}
ctx.print(`function ${stmt.name}(`);
this._visitParams(stmt.params, ctx);
ctx.print(`):`);
if (isPresent(stmt.type)) {
stmt.type.visitType(this, ctx);
} else {
ctx.print(`void`);
}
ctx.println(` {`);
ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent();
ctx.println(`}`);
return null;
}
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`);
ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.incIndent();
var catchStmts = [
<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack'))
.toDeclStmt(null, [o.StmtModifier.Final])
].concat(stmt.catchStmts);
this.visitAllStatements(catchStmts, ctx);
ctx.decIndent();
ctx.println(`}`);
return null;
}
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
var typeStr;
switch (type.name) {
case o.BuiltinTypeName.Bool:
typeStr = 'boolean';
break;
case o.BuiltinTypeName.Dynamic:
typeStr = 'any';
break;
case o.BuiltinTypeName.Function:
typeStr = 'Function';
break;
case o.BuiltinTypeName.Number:
typeStr = 'number';
break;
case o.BuiltinTypeName.Int:
typeStr = 'number';
break;
case o.BuiltinTypeName.String:
typeStr = 'string';
break;
default:
throw new BaseException(`Unsupported builtin type ${type.name}`);
}
ctx.print(typeStr);
return null;
}
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;
}
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
if (isPresent(type.of)) {
type.of.visitType(this, ctx);
} else {
ctx.print(`any`);
}
ctx.print(`[]`);
return null;
}
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
ctx.print(`{[key: string]:`);
if (isPresent(type.valueType)) {
type.valueType.visitType(this, ctx);
} else {
ctx.print(`any`);
}
ctx.print(`}`);
return null;
}
getBuiltinMethodName(method: o.BuiltinMethod): string {
var name;
switch (method) {
case o.BuiltinMethod.ConcatArray:
name = 'concat';
break;
case o.BuiltinMethod.SubscribeObservable:
name = 'subscribe';
break;
case o.BuiltinMethod.bind:
name = 'bind';
break;
default:
throw new BaseException(`Unknown builtin method: ${method}`);
}
return name;
}
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects((param) => {
ctx.print(param.name);
if (isPresent(param.type)) {
ctx.print(`:`);
param.type.visitType(this, ctx);
}
}, params, ctx, ',');
}
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
ctx: EmitterVisitorContext): void {
if (isBlank(value.name)) {
throw new BaseException(`Internal error: unknown identifier ${value}`);
}
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(value.moduleUrl, prefix);
}
ctx.print(`${prefix}.`);
}
ctx.print(value.name);
if (isPresent(typeParams) && typeParams.length > 0) {
ctx.print(`<`);
this.visitAllObjects((type) => type.visitType(this, ctx), typeParams, ctx, ',');
ctx.print(`>`);
}
}
}

View File

@ -1,420 +0,0 @@
import {isPresent, isBlank, isArray, normalizeBlank} from '../src/facade/lang';
import {ListWrapper} from '../src/facade/collection';
import {
VariableAst,
AttrAst,
DirectiveAst,
ProviderAst,
ProviderAstType,
ReferenceAst
} from './template_ast';
import {
CompileTypeMetadata,
CompileTokenMap,
CompileQueryMetadata,
CompileTokenMetadata,
CompileProviderMetadata,
CompileDirectiveMetadata,
CompileDiDependencyMetadata
} from './compile_metadata';
import {Identifiers, identifierToken} from './identifiers';
import {ParseSourceSpan, ParseError} from './parse_util';
export class ProviderError extends ParseError {
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
}
export class ProviderViewContext {
/**
* @internal
*/
viewQueries: CompileTokenMap<CompileQueryMetadata[]>;
/**
* @internal
*/
viewProviders: CompileTokenMap<boolean>;
errors: ProviderError[] = [];
constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) {
this.viewQueries = _getViewQueries(component);
this.viewProviders = new CompileTokenMap<boolean>();
_normalizeProviders(component.viewProviders, sourceSpan, this.errors)
.forEach((provider) => {
if (isBlank(this.viewProviders.get(provider.token))) {
this.viewProviders.add(provider.token, true);
}
});
}
}
export class ProviderElementContext {
private _contentQueries: CompileTokenMap<CompileQueryMetadata[]>;
private _transformedProviders = new CompileTokenMap<ProviderAst>();
private _seenProviders = new CompileTokenMap<boolean>();
private _allProviders: CompileTokenMap<ProviderAst>;
private _attrs: {[key: string]: string};
private _hasViewContainer: boolean = false;
constructor(private _viewContext: ProviderViewContext, private _parent: ProviderElementContext,
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[],
attrs: AttrAst[], refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) {
this._attrs = {};
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
this._allProviders =
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors);
this._contentQueries = _getContentQueries(directivesMeta);
var queriedTokens = new CompileTokenMap<boolean>();
this._allProviders.values().forEach(
(provider) => { this._addQueryReadsTo(provider.token, queriedTokens); });
refs.forEach((refAst) => {
this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens);
});
if (isPresent(queriedTokens.get(identifierToken(Identifiers.ViewContainerRef)))) {
this._hasViewContainer = true;
}
// create the providers that we know are eager first
this._allProviders.values().forEach((provider) => {
var eager = provider.eager || isPresent(queriedTokens.get(provider.token));
if (eager) {
this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
}
});
}
afterElement() {
// collect lazy providers
this._allProviders.values().forEach((provider) => {
this._getOrCreateLocalProvider(provider.providerType, provider.token, false);
});
}
get transformProviders(): ProviderAst[] { return this._transformedProviders.values(); }
get transformedDirectiveAsts(): DirectiveAst[] {
var sortedProviderTypes =
this._transformedProviders.values().map(provider => provider.token.identifier);
var sortedDirectives = ListWrapper.clone(this._directiveAsts);
ListWrapper.sort(sortedDirectives,
(dir1, dir2) => sortedProviderTypes.indexOf(dir1.directive.type) -
sortedProviderTypes.indexOf(dir2.directive.type));
return sortedDirectives;
}
get transformedHasViewContainer(): boolean { return this._hasViewContainer; }
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: CompileTokenMap<boolean>) {
this._getQueriesFor(token).forEach((query) => {
var queryReadToken = isPresent(query.read) ? query.read : token;
if (isBlank(queryReadTokens.get(queryReadToken))) {
queryReadTokens.add(queryReadToken, true);
}
});
}
private _getQueriesFor(token: CompileTokenMetadata): CompileQueryMetadata[] {
var result: CompileQueryMetadata[] = [];
var currentEl: ProviderElementContext = this;
var distance = 0;
var queries: CompileQueryMetadata[];
while (currentEl !== null) {
queries = currentEl._contentQueries.get(token);
if (isPresent(queries)) {
ListWrapper.addAll(result, queries.filter((query) => query.descendants || distance <= 1));
}
if (currentEl._directiveAsts.length > 0) {
distance++;
}
currentEl = currentEl._parent;
}
queries = this._viewContext.viewQueries.get(token);
if (isPresent(queries)) {
ListWrapper.addAll(result, queries);
}
return result;
}
private _getOrCreateLocalProvider(requestingProviderType: ProviderAstType,
token: CompileTokenMetadata, eager: boolean): ProviderAst {
var resolvedProvider = this._allProviders.get(token);
if (isBlank(resolvedProvider) ||
((requestingProviderType === ProviderAstType.Directive ||
requestingProviderType === ProviderAstType.PublicService) &&
resolvedProvider.providerType === ProviderAstType.PrivateService) ||
((requestingProviderType === ProviderAstType.PrivateService ||
requestingProviderType === ProviderAstType.PublicService) &&
resolvedProvider.providerType === ProviderAstType.Builtin)) {
return null;
}
var transformedProviderAst = this._transformedProviders.get(token);
if (isPresent(transformedProviderAst)) {
return transformedProviderAst;
}
if (isPresent(this._seenProviders.get(token))) {
this._viewContext.errors.push(new ProviderError(
`Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan));
return null;
}
this._seenProviders.add(token, true);
var transformedProviders = resolvedProvider.providers.map((provider) => {
var transformedUseValue = provider.useValue;
var transformedUseExisting = provider.useExisting;
var transformedDeps;
if (isPresent(provider.useExisting)) {
var existingDiDep = this._getDependency(
resolvedProvider.providerType,
new CompileDiDependencyMetadata({token: provider.useExisting}), eager);
if (isPresent(existingDiDep.token)) {
transformedUseExisting = existingDiDep.token;
} else {
transformedUseExisting = null;
transformedUseValue = existingDiDep.value;
}
} else if (isPresent(provider.useFactory)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
} else if (isPresent(provider.useClass)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
}
return _transformProvider(provider, {
useExisting: transformedUseExisting,
useValue: transformedUseValue,
deps: transformedDeps
});
});
transformedProviderAst =
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
this._transformedProviders.add(token, transformedProviderAst);
return transformedProviderAst;
}
private _getLocalDependency(requestingProviderType: ProviderAstType,
dep: CompileDiDependencyMetadata,
eager: boolean = null): CompileDiDependencyMetadata {
if (dep.isAttribute) {
var attrValue = this._attrs[dep.token.value];
return new CompileDiDependencyMetadata({isValue: true, value: normalizeBlank(attrValue)});
}
if (isPresent(dep.query) || isPresent(dep.viewQuery)) {
return dep;
}
if (isPresent(dep.token)) {
// access builtints
if ((requestingProviderType === ProviderAstType.Directive ||
requestingProviderType === ProviderAstType.Component)) {
if (dep.token.equalsTo(identifierToken(Identifiers.Renderer)) ||
dep.token.equalsTo(identifierToken(Identifiers.ElementRef)) ||
dep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef)) ||
dep.token.equalsTo(identifierToken(Identifiers.TemplateRef))) {
return dep;
}
if (dep.token.equalsTo(identifierToken(Identifiers.ViewContainerRef))) {
this._hasViewContainer = true;
}
}
// access the injector
if (dep.token.equalsTo(identifierToken(Identifiers.Injector))) {
return dep;
}
// access providers
if (isPresent(this._getOrCreateLocalProvider(requestingProviderType, dep.token, eager))) {
return dep;
}
}
return null;
}
private _getDependency(requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata,
eager: boolean = null): CompileDiDependencyMetadata {
var currElement: ProviderElementContext = this;
var currEager: boolean = eager;
var result: CompileDiDependencyMetadata = null;
if (!dep.isSkipSelf) {
result = this._getLocalDependency(requestingProviderType, dep, eager);
}
if (dep.isSelf) {
if (isBlank(result) && dep.isOptional) {
result = new CompileDiDependencyMetadata({isValue: true, value: null});
}
} else {
// check parent elements
while (isBlank(result) && isPresent(currElement._parent)) {
var prevElement = currElement;
currElement = currElement._parent;
if (prevElement._isViewRoot) {
currEager = false;
}
result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager);
}
// check @Host restriction
if (isBlank(result)) {
if (!dep.isHost || this._viewContext.component.type.isHost ||
identifierToken(this._viewContext.component.type).equalsTo(dep.token) ||
isPresent(this._viewContext.viewProviders.get(dep.token))) {
result = dep;
} else {
result = dep.isOptional ?
result = new CompileDiDependencyMetadata({isValue: true, value: null}) :
null;
}
}
}
if (isBlank(result)) {
this._viewContext.errors.push(
new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan));
}
return result;
}
}
function _transformProvider(
provider: CompileProviderMetadata,
{useExisting, useValue, deps}:
{useExisting: CompileTokenMetadata, useValue: any, deps: CompileDiDependencyMetadata[]}) {
return new CompileProviderMetadata({
token: provider.token,
useClass: provider.useClass,
useExisting: useExisting,
useFactory: provider.useFactory,
useValue: useValue,
deps: deps,
multi: provider.multi
});
}
function _transformProviderAst(
provider: ProviderAst,
{eager, providers}: {eager: boolean, providers: CompileProviderMetadata[]}): ProviderAst {
return new ProviderAst(provider.token, provider.multiProvider, provider.eager || eager, providers,
provider.providerType, provider.sourceSpan);
}
function _normalizeProviders(
providers: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] {
if (isBlank(targetProviders)) {
targetProviders = [];
}
if (isPresent(providers)) {
providers.forEach((provider) => {
if (isArray(provider)) {
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
} else {
var normalizeProvider: CompileProviderMetadata;
if (provider instanceof CompileProviderMetadata) {
normalizeProvider = provider;
} else if (provider instanceof CompileTypeMetadata) {
normalizeProvider = new CompileProviderMetadata(
{token: new CompileTokenMetadata({identifier: provider}), useClass: provider});
} else {
targetErrors.push(new ProviderError(`Unknown provider type ${provider}`, sourceSpan));
}
if (isPresent(normalizeProvider)) {
targetProviders.push(normalizeProvider);
}
}
});
}
return targetProviders;
}
function _resolveProvidersFromDirectives(directives: CompileDirectiveMetadata[],
sourceSpan: ParseSourceSpan,
targetErrors: ParseError[]): CompileTokenMap<ProviderAst> {
var providersByToken = new CompileTokenMap<ProviderAst>();
directives.forEach((directive) => {
var dirProvider = new CompileProviderMetadata(
{token: new CompileTokenMetadata({identifier: directive.type}), useClass: directive.type});
_resolveProviders([dirProvider],
directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive,
true, sourceSpan, targetErrors, providersByToken);
});
// Note: directives need to be able to overwrite providers of a component!
var directivesWithComponentFirst =
directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
directivesWithComponentFirst.forEach((directive) => {
_resolveProviders(_normalizeProviders(directive.providers, sourceSpan, targetErrors),
ProviderAstType.PublicService, false, sourceSpan, targetErrors,
providersByToken);
_resolveProviders(_normalizeProviders(directive.viewProviders, sourceSpan, targetErrors),
ProviderAstType.PrivateService, false, sourceSpan, targetErrors,
providersByToken);
});
return providersByToken;
}
function _resolveProviders(providers: CompileProviderMetadata[], providerType: ProviderAstType,
eager: boolean, sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
targetProvidersByToken: CompileTokenMap<ProviderAst>) {
providers.forEach((provider) => {
var resolvedProvider = targetProvidersByToken.get(provider.token);
if (isPresent(resolvedProvider) && resolvedProvider.multiProvider !== provider.multi) {
targetErrors.push(new ProviderError(
`Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`,
sourceSpan));
}
if (isBlank(resolvedProvider)) {
resolvedProvider = new ProviderAst(provider.token, provider.multi, eager, [provider],
providerType, sourceSpan);
targetProvidersByToken.add(provider.token, resolvedProvider);
} else {
if (!provider.multi) {
ListWrapper.clear(resolvedProvider.providers);
}
resolvedProvider.providers.push(provider);
}
});
}
function _getViewQueries(
component: CompileDirectiveMetadata): CompileTokenMap<CompileQueryMetadata[]> {
var viewQueries = new CompileTokenMap<CompileQueryMetadata[]>();
if (isPresent(component.viewQueries)) {
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
}
component.type.diDeps.forEach((dep) => {
if (isPresent(dep.viewQuery)) {
_addQueryToTokenMap(viewQueries, dep.viewQuery);
}
});
return viewQueries;
}
function _getContentQueries(
directives: CompileDirectiveMetadata[]): CompileTokenMap<CompileQueryMetadata[]> {
var contentQueries = new CompileTokenMap<CompileQueryMetadata[]>();
directives.forEach(directive => {
if (isPresent(directive.queries)) {
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
}
directive.type.diDeps.forEach((dep) => {
if (isPresent(dep.query)) {
_addQueryToTokenMap(contentQueries, dep.query);
}
});
});
return contentQueries;
}
function _addQueryToTokenMap(map: CompileTokenMap<CompileQueryMetadata[]>,
query: CompileQueryMetadata) {
query.selectors.forEach((token: CompileTokenMetadata) => {
var entry = map.get(token);
if (isBlank(entry)) {
entry = [];
map.add(token, entry);
}
entry.push(query);
});
}

View File

@ -1,210 +0,0 @@
import {Injectable, ComponentFactory, ComponentResolver} from '@angular/core';
import {
IS_DART,
Type,
isBlank,
} from '../src/facade/lang';
import {BaseException} from '../src/facade/exceptions';
import {
ListWrapper,
} from '../src/facade/collection';
import {PromiseWrapper} from '../src/facade/async';
import {
createHostComponentMeta,
CompileDirectiveMetadata,
CompilePipeMetadata,
CompileIdentifierMetadata
} from './compile_metadata';
import {
TemplateAst,
} from './template_ast';
import {StyleCompiler, StylesCompileDependency, StylesCompileResult} from './style_compiler';
import {ViewCompiler} from './view_compiler/view_compiler';
import {TemplateParser} from './template_parser';
import {DirectiveNormalizer} from './directive_normalizer';
import {CompileMetadataResolver} from './metadata_resolver';
import {CompilerConfig} from './config';
import * as ir from './output/output_ast';
import {jitStatements} from './output/output_jit';
import {interpretStatements} from './output/output_interpreter';
import {InterpretiveAppViewInstanceFactory} from './output/interpretive_view';
import {XHR} from './xhr';
/**
* An internal module of the Angular compiler that begins with component types,
* extracts templates, and eventually produces a compiled version of the component
* ready for linking into an application.
*/
@Injectable()
export class RuntimeCompiler implements ComponentResolver {
private _styleCache: Map<string, Promise<string>> = new Map<string, Promise<string>>();
private _hostCacheKeys = new Map<Type, any>();
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
constructor(private _metadataResolver: CompileMetadataResolver,
private _templateNormalizer: DirectiveNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _xhr: XHR,
private _genConfig: CompilerConfig) {}
resolveComponent(componentType: Type): Promise<ComponentFactory<any>> {
var compMeta: CompileDirectiveMetadata =
this._metadataResolver.getDirectiveMetadata(componentType);
var hostCacheKey = this._hostCacheKeys.get(componentType);
if (isBlank(hostCacheKey)) {
hostCacheKey = new Object();
this._hostCacheKeys.set(componentType, hostCacheKey);
assertComponent(compMeta);
var hostMeta: CompileDirectiveMetadata =
createHostComponentMeta(compMeta.type, compMeta.selector);
this._loadAndCompileComponent(hostCacheKey, hostMeta, [compMeta], [], []);
}
return this._compiledTemplateDone.get(hostCacheKey)
.then((compiledTemplate: CompiledTemplate) => new ComponentFactory(
compMeta.selector, compiledTemplate.viewFactory, componentType));
}
clearCache() {
this._styleCache.clear();
this._compiledTemplateCache.clear();
this._compiledTemplateDone.clear();
this._hostCacheKeys.clear();
}
private _loadAndCompileComponent(cacheKey: any, compMeta: CompileDirectiveMetadata,
viewDirectives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[],
compilingComponentsPath: any[]): CompiledTemplate {
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
var done = this._compiledTemplateDone.get(cacheKey);
if (isBlank(compiledTemplate)) {
compiledTemplate = new CompiledTemplate();
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
done =
PromiseWrapper.all(
[<any>this._compileComponentStyles(compMeta)].concat(viewDirectives.map(
dirMeta => this._templateNormalizer.normalizeDirective(dirMeta))))
.then((stylesAndNormalizedViewDirMetas: any[]) => {
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
var styles = stylesAndNormalizedViewDirMetas[0];
var parsedTemplate =
this._templateParser.parse(compMeta, compMeta.template.template,
normalizedViewDirMetas, pipes, compMeta.type.name);
var childPromises = [];
compiledTemplate.init(this._compileComponent(compMeta, parsedTemplate, styles,
pipes, compilingComponentsPath,
childPromises));
return PromiseWrapper.all(childPromises).then((_) => { return compiledTemplate; });
});
this._compiledTemplateDone.set(cacheKey, done);
}
return compiledTemplate;
}
private _compileComponent(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
styles: string[], pipes: CompilePipeMetadata[],
compilingComponentsPath: any[],
childPromises: Promise<any>[]): Function {
var compileResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate,
new ir.ExternalExpr(new CompileIdentifierMetadata({runtime: styles})), pipes);
compileResult.dependencies.forEach((dep) => {
var childCompilingComponentsPath = ListWrapper.clone(compilingComponentsPath);
var childCacheKey = dep.comp.type.runtime;
var childViewDirectives: CompileDirectiveMetadata[] =
this._metadataResolver.getViewDirectivesMetadata(dep.comp.type.runtime);
var childViewPipes: CompilePipeMetadata[] =
this._metadataResolver.getViewPipesMetadata(dep.comp.type.runtime);
var childIsRecursive = ListWrapper.contains(childCompilingComponentsPath, childCacheKey);
childCompilingComponentsPath.push(childCacheKey);
var childComp =
this._loadAndCompileComponent(dep.comp.type.runtime, dep.comp, childViewDirectives,
childViewPipes, childCompilingComponentsPath);
dep.factoryPlaceholder.runtime = childComp.proxyViewFactory;
dep.factoryPlaceholder.name = `viewFactory_${dep.comp.type.name}`;
if (!childIsRecursive) {
// Only wait for a child if it is not a cycle
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
}
});
var factory;
if (IS_DART || !this._genConfig.useJit) {
factory = interpretStatements(compileResult.statements, compileResult.viewFactoryVar,
new InterpretiveAppViewInstanceFactory());
} else {
factory = jitStatements(`${compMeta.type.name}.template.js`, compileResult.statements,
compileResult.viewFactoryVar);
}
return factory;
}
private _compileComponentStyles(compMeta: CompileDirectiveMetadata): Promise<string[]> {
var compileResult = this._styleCompiler.compileComponent(compMeta);
return this._resolveStylesCompileResult(compMeta.type.name, compileResult);
}
private _resolveStylesCompileResult(sourceUrl: string,
result: StylesCompileResult): Promise<string[]> {
var promises = result.dependencies.map((dep) => this._loadStylesheetDep(dep));
return PromiseWrapper.all(promises)
.then((cssTexts) => {
var nestedCompileResultPromises = [];
for (var i = 0; i < result.dependencies.length; i++) {
var dep = result.dependencies[i];
var cssText = cssTexts[i];
var nestedCompileResult =
this._styleCompiler.compileStylesheet(dep.moduleUrl, cssText, dep.isShimmed);
nestedCompileResultPromises.push(
this._resolveStylesCompileResult(dep.moduleUrl, nestedCompileResult));
}
return PromiseWrapper.all(nestedCompileResultPromises);
})
.then((nestedStylesArr) => {
for (var i = 0; i < result.dependencies.length; i++) {
var dep = result.dependencies[i];
dep.valuePlaceholder.runtime = nestedStylesArr[i];
dep.valuePlaceholder.name = `importedStyles${i}`;
}
if (IS_DART || !this._genConfig.useJit) {
return interpretStatements(result.statements, result.stylesVar,
new InterpretiveAppViewInstanceFactory());
} else {
return jitStatements(`${sourceUrl}.css.js`, result.statements, result.stylesVar);
}
});
}
private _loadStylesheetDep(dep: StylesCompileDependency): Promise<string> {
var cacheKey = `${dep.moduleUrl}${dep.isShimmed ? '.shim' : ''}`;
var cssTextPromise = this._styleCache.get(cacheKey);
if (isBlank(cssTextPromise)) {
cssTextPromise = this._xhr.get(dep.moduleUrl);
this._styleCache.set(cacheKey, cssTextPromise);
}
return cssTextPromise;
}
}
class CompiledTemplate {
viewFactory: Function = null;
proxyViewFactory: Function;
constructor() {
this.proxyViewFactory = (viewUtils, childInjector, contextEl) =>
this.viewFactory(viewUtils, childInjector, contextEl);
}
init(viewFactory: Function) { this.viewFactory = viewFactory; }
}
function assertComponent(meta: CompileDirectiveMetadata) {
if (!meta.isComponent) {
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
}
}

View File

@ -1,281 +0,0 @@
import {Injectable} from '@angular/core';
import {SecurityContext} from '../../core_private';
import {isPresent} from '../facade/lang';
import {StringMapWrapper} from '../facade/collection';
import {ElementSchemaRegistry} from './element_schema_registry';
const EVENT = 'event';
const BOOLEAN = 'boolean';
const NUMBER = 'number';
const STRING = 'string';
const OBJECT = 'object';
/**
* This array represents the DOM schema. It encodes inheritance, properties, and events.
*
* ## Overview
*
* Each line represents one kind of element. The `element_inheritance` and properties are joined
* using `element_inheritance|preperties` syntax.
*
* ## Element Inheritance
*
* The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
* Here the individual elements are separated by `,` (commas). Every element in the list
* has identical properties.
*
* An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
* specified then `""` (blank) element is assumed.
*
* NOTE: The blank element inherits from root `*` element, the super element of all elements.
*
* NOTE an element prefix such as `@svg:` has no special meaning to the schema.
*
* ## Properties
*
* Each element has a set of properties separated by `,` (commas). Each property can be prefixed
* by a special character designating its type:
*
* - (no prefix): property is a string.
* - `*`: property represents an event.
* - `!`: property is a boolean.
* - `#`: property is a number.
* - `%`: property is an object.
*
* ## Query
*
* The class creates an internal squas representaino which allows to easily answer the query of
* if a given property exist on a given element.
*
* NOTE: We don't yet support querying for types or events.
* NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder.
*/
const SCHEMA: string[] =
/*@ts2dart_const*/ ([
'*|%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop',
'^*|accessKey,contentEditable,dir,!draggable,!hidden,innerText,lang,*abort,*autocomplete,*autocompleteerror,*beforecopy,*beforecut,*beforepaste,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*message,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*paste,*pause,*play,*playing,*progress,*ratechange,*reset,*resize,*scroll,*search,*seeked,*seeking,*select,*selectstart,*show,*stalled,*submit,*suspend,*timeupdate,*toggle,*volumechange,*waiting,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate',
'media|!autoplay,!controls,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,#playbackRate,preload,src,#volume',
'@svg:^*|*abort,*autocomplete,*autocompleteerror,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*cuechange,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*pause,*play,*playing,*progress,*ratechange,*reset,*resize,*scroll,*seeked,*seeking,*select,*show,*stalled,*submit,*suspend,*timeupdate,*toggle,*volumechange,*waiting,%style,#tabIndex',
'@svg:graphics^@svg:|',
'@svg:animation^@svg:|*begin,*end,*repeat',
'@svg:geometry^@svg:|',
'@svg:componentTransferFunction^@svg:|',
'@svg:gradient^@svg:|',
'@svg:textContent^@svg:graphics|',
'@svg:textPositioning^@svg:textContent|',
'a|charset,coords,download,hash,host,hostname,href,hreflang,name,password,pathname,ping,port,protocol,rel,rev,search,shape,target,text,type,username',
'area|alt,coords,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,search,shape,target,username',
'audio^media|',
'br|clear',
'base|href,target',
'body|aLink,background,bgColor,link,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink',
'button|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
'canvas|#height,#width',
'content|select',
'dl|!compact',
'datalist|',
'details|!open',
'dialog|!open,returnValue',
'dir|!compact',
'div|align',
'embed|align,height,name,src,type,width',
'fieldset|!disabled,name',
'font|color,face,size',
'form|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
'frame|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
'frameset|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
'hr|align,color,!noShade,size,width',
'head|',
'h1,h2,h3,h4,h5,h6|align',
'html|version',
'iframe|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,%sandbox,scrolling,src,srcdoc,width',
'img|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,sizes,src,srcset,useMap,#vspace,#width',
'input|accept,align,alt,autocapitalize,autocomplete,!autofocus,!checked,!defaultChecked,defaultValue,dirName,!disabled,%files,formAction,formEnctype,formMethod,!formNoValidate,formTarget,#height,!incremental,!indeterminate,max,#maxLength,min,#minLength,!multiple,name,pattern,placeholder,!readOnly,!required,selectionDirection,#selectionEnd,#selectionStart,#size,src,step,type,useMap,value,%valueAsDate,#valueAsNumber,#width',
'keygen|!autofocus,challenge,!disabled,keytype,name',
'li|type,#value',
'label|htmlFor',
'legend|align',
'link|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,rel,%relList,rev,%sizes,target,type',
'map|name',
'marquee|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
'menu|!compact',
'meta|content,httpEquiv,name,scheme',
'meter|#high,#low,#max,#min,#optimum,#value',
'ins,del|cite,dateTime',
'ol|!compact,!reversed,#start,type',
'object|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
'optgroup|!disabled,label',
'option|!defaultSelected,!disabled,label,!selected,text,value',
'output|defaultValue,%htmlFor,name,value',
'p|align',
'param|name,type,value,valueType',
'picture|',
'pre|#width',
'progress|#max,#value',
'q,blockquote,cite|',
'script|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
'select|!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
'shadow|',
'source|media,sizes,src,srcset,type',
'span|',
'style|!disabled,media,type',
'caption|align',
'th,td|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
'col,colgroup|align,ch,chOff,#span,vAlign,width',
'table|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
'tr|align,bgColor,ch,chOff,vAlign',
'tfoot,thead,tbody|align,ch,chOff,vAlign',
'template|',
'textarea|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
'title|text',
'track|!default,kind,label,src,srclang',
'ul|!compact,type',
'unknown|',
'video^media|#height,poster,#width',
'@svg:a^@svg:graphics|',
'@svg:animate^@svg:animation|',
'@svg:animateMotion^@svg:animation|',
'@svg:animateTransform^@svg:animation|',
'@svg:circle^@svg:geometry|',
'@svg:clipPath^@svg:graphics|',
'@svg:cursor^@svg:|',
'@svg:defs^@svg:graphics|',
'@svg:desc^@svg:|',
'@svg:discard^@svg:|',
'@svg:ellipse^@svg:geometry|',
'@svg:feBlend^@svg:|',
'@svg:feColorMatrix^@svg:|',
'@svg:feComponentTransfer^@svg:|',
'@svg:feComposite^@svg:|',
'@svg:feConvolveMatrix^@svg:|',
'@svg:feDiffuseLighting^@svg:|',
'@svg:feDisplacementMap^@svg:|',
'@svg:feDistantLight^@svg:|',
'@svg:feDropShadow^@svg:|',
'@svg:feFlood^@svg:|',
'@svg:feFuncA^@svg:componentTransferFunction|',
'@svg:feFuncB^@svg:componentTransferFunction|',
'@svg:feFuncG^@svg:componentTransferFunction|',
'@svg:feFuncR^@svg:componentTransferFunction|',
'@svg:feGaussianBlur^@svg:|',
'@svg:feImage^@svg:|',
'@svg:feMerge^@svg:|',
'@svg:feMergeNode^@svg:|',
'@svg:feMorphology^@svg:|',
'@svg:feOffset^@svg:|',
'@svg:fePointLight^@svg:|',
'@svg:feSpecularLighting^@svg:|',
'@svg:feSpotLight^@svg:|',
'@svg:feTile^@svg:|',
'@svg:feTurbulence^@svg:|',
'@svg:filter^@svg:|',
'@svg:foreignObject^@svg:graphics|',
'@svg:g^@svg:graphics|',
'@svg:image^@svg:graphics|',
'@svg:line^@svg:geometry|',
'@svg:linearGradient^@svg:gradient|',
'@svg:mpath^@svg:|',
'@svg:marker^@svg:|',
'@svg:mask^@svg:|',
'@svg:metadata^@svg:|',
'@svg:path^@svg:geometry|',
'@svg:pattern^@svg:|',
'@svg:polygon^@svg:geometry|',
'@svg:polyline^@svg:geometry|',
'@svg:radialGradient^@svg:gradient|',
'@svg:rect^@svg:geometry|',
'@svg:svg^@svg:graphics|#currentScale,#zoomAndPan',
'@svg:script^@svg:|type',
'@svg:set^@svg:animation|',
'@svg:stop^@svg:|',
'@svg:style^@svg:|!disabled,media,title,type',
'@svg:switch^@svg:graphics|',
'@svg:symbol^@svg:|',
'@svg:tspan^@svg:textPositioning|',
'@svg:text^@svg:textPositioning|',
'@svg:textPath^@svg:textContent|',
'@svg:title^@svg:|',
'@svg:use^@svg:graphics|',
'@svg:view^@svg:|#zoomAndPan'
]);
var attrToPropMap: {[name: string]: string} = <any>{
'class': 'className',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex'
};
@Injectable()
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
schema = <{[element: string]: {[property: string]: string}}>{};
constructor() {
super();
SCHEMA.forEach(encodedType => {
var parts = encodedType.split('|');
var properties = parts[1].split(',');
var typeParts = (parts[0] + '^').split('^');
var typeName = typeParts[0];
var type = <{[property: string]: string}>{};
typeName.split(',').forEach(tag => this.schema[tag] = type);
var superType = this.schema[typeParts[1]];
if (isPresent(superType)) {
StringMapWrapper.forEach(superType, (v, k) => type[k] = v);
}
properties.forEach((property: string) => {
if (property == '') {
} else if (property.startsWith('*')) {
// We don't yet support events.
// type[property.substring(1)] = EVENT;
} else if (property.startsWith('!')) {
type[property.substring(1)] = BOOLEAN;
} else if (property.startsWith('#')) {
type[property.substring(1)] = NUMBER;
} else if (property.startsWith('%')) {
type[property.substring(1)] = OBJECT;
} else {
type[property] = STRING;
}
});
});
}
hasProperty(tagName: string, propName: string): boolean {
if (tagName.indexOf('-') !== -1) {
// can't tell now as we don't know which properties a custom element will get
// once it is instantiated
return true;
} else {
var elementProperties = this.schema[tagName.toLowerCase()];
if (!isPresent(elementProperties)) {
elementProperties = this.schema['unknown'];
}
return isPresent(elementProperties[propName]);
}
}
/**
* securityContext returns the security context for the given property on the given DOM tag.
*
* Tag and property name are statically known and cannot change at runtime, i.e. it is not
* possible to bind a value into a changing attribute or tag name.
*
* The filtering is white list based. All attributes in the schema above are assumed to have the
* 'NONE' security context, i.e. that they are safe inert string values. Only specific well known
* attack vectors are assigned their appropriate context.
*/
securityContext(tagName: string, propName: string): SecurityContext {
// TODO(martinprobst): Fill in missing properties.
if (propName === 'style') return SecurityContext.STYLE;
if (tagName === 'a' && propName === 'href') return SecurityContext.URL;
if (propName === 'innerHTML') return SecurityContext.HTML;
return SecurityContext.NONE;
}
getMappedPropName(propName: string): string {
var mappedPropName = StringMapWrapper.get(attrToPropMap, propName);
return isPresent(mappedPropName) ? mappedPropName : propName;
}
}

View File

@ -1,5 +0,0 @@
export abstract class ElementSchemaRegistry {
abstract hasProperty(tagName: string, propName: string): boolean;
abstract securityContext(tagName: string, propName: string): any;
abstract getMappedPropName(propName: string): string;
}

View File

@ -1,72 +0,0 @@
import {ViewEncapsulation, Injectable} from '@angular/core';
import {CompileIdentifierMetadata, CompileDirectiveMetadata} from './compile_metadata';
import * as o from './output/output_ast';
import {ShadowCss} from './shadow_css';
import {UrlResolver} from './url_resolver';
import {extractStyleUrls} from './style_url_resolver';
import {isPresent} from '../src/facade/lang';
const COMPONENT_VARIABLE = '%COMP%';
const HOST_ATTR = /*@ts2dart_const*/ `_nghost-${COMPONENT_VARIABLE}`;
const CONTENT_ATTR = /*@ts2dart_const*/ `_ngcontent-${COMPONENT_VARIABLE}`;
export class StylesCompileDependency {
constructor(public moduleUrl: string, public isShimmed: boolean,
public valuePlaceholder: CompileIdentifierMetadata) {}
}
export class StylesCompileResult {
constructor(public statements: o.Statement[], public stylesVar: string,
public dependencies: StylesCompileDependency[]) {}
}
@Injectable()
export class StyleCompiler {
private _shadowCss: ShadowCss = new ShadowCss();
constructor(private _urlResolver: UrlResolver) {}
compileComponent(comp: CompileDirectiveMetadata): StylesCompileResult {
var shim = comp.template.encapsulation === ViewEncapsulation.Emulated;
return this._compileStyles(getStylesVarName(comp), comp.template.styles,
comp.template.styleUrls, shim);
}
compileStylesheet(stylesheetUrl: string, cssText: string,
isShimmed: boolean): StylesCompileResult {
var styleWithImports = extractStyleUrls(this._urlResolver, stylesheetUrl, cssText);
return this._compileStyles(getStylesVarName(null), [styleWithImports.style],
styleWithImports.styleUrls, isShimmed);
}
private _compileStyles(stylesVar: string, plainStyles: string[], absUrls: string[],
shim: boolean): StylesCompileResult {
var styleExpressions =
plainStyles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
var dependencies = [];
for (var i = 0; i < absUrls.length; i++) {
var identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)});
dependencies.push(new StylesCompileDependency(absUrls[i], shim, identifier));
styleExpressions.push(new o.ExternalExpr(identifier));
}
// styles variable contains plain strings and arrays of other styles arrays (recursive),
// so we set its type to dynamic.
var stmt = o.variable(stylesVar)
.set(o.literalArr(styleExpressions,
new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]);
return new StylesCompileResult([stmt], stylesVar, dependencies);
}
private _shimIfNeeded(style: string, shim: boolean): string {
return shim ? this._shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR) : style;
}
}
function getStylesVarName(component: CompileDirectiveMetadata): string {
var result = `styles`;
if (isPresent(component)) {
result += `_${component.type.name}`;
}
return result;
}

View File

@ -1,87 +0,0 @@
import {
IS_DART,
StringWrapper,
Math,
isBlank,
isArray,
isStrictStringMap,
isPrimitive
} from './facade/lang';
import {StringMapWrapper} from './facade/collection';
export var MODULE_SUFFIX = IS_DART ? '.dart' : '';
var CAMEL_CASE_REGEXP = /([A-Z])/g;
var DASH_CASE_REGEXP = /-([a-z])/g;
export function camelCaseToDashCase(input: string): string {
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
(m) => { return '-' + m[1].toLowerCase(); });
}
export function dashCaseToCamelCase(input: string): string {
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP,
(m) => { return m[1].toUpperCase(); });
}
export function splitAtColon(input: string, defaultValues: string[]): string[] {
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
if (parts.length > 1) {
return parts;
} else {
return defaultValues;
}
}
export function sanitizeIdentifier(name: string): string {
return StringWrapper.replaceAll(name, /\W/g, '_');
}
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
if (isArray(value)) {
return visitor.visitArray(<any[]>value, context);
} else if (isStrictStringMap(value)) {
return visitor.visitStringMap(<{[key: string]: any}>value, context);
} else if (isBlank(value) || isPrimitive(value)) {
return visitor.visitPrimitive(value, context);
} else {
return visitor.visitOther(value, context);
}
}
export interface ValueVisitor {
visitArray(arr: any[], context: any): any;
visitStringMap(map: {[key: string]: any}, context: any): any;
visitPrimitive(value: any, context: any): any;
visitOther(value: any, context: any): any;
}
export class ValueTransformer implements ValueVisitor {
visitArray(arr: any[], context: any): any {
return arr.map(value => visitValue(value, this, context));
}
visitStringMap(map: {[key: string]: any}, context: any): any {
var result = {};
StringMapWrapper.forEach(map,
(value, key) => { result[key] = visitValue(value, this, context); });
return result;
}
visitPrimitive(value: any, context: any): any { return value; }
visitOther(value: any, context: any): any { return value; }
}
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
if (IS_DART) {
if (path == null) {
return `asset:angular2/${pkg}/${pkg}.dart`;
} else {
return `asset:angular2/lib/${pkg}/src/${path}.dart`;
}
} else {
if (path == null) {
return `asset:@angular/lib/${pkg}/index`;
} else {
return `asset:@angular/lib/${pkg}/src/${path}`;
}
}
}

View File

@ -1,6 +0,0 @@
import {CompileNode} from './compile_element';
import {TemplateAst} from '../template_ast';
export class CompileBinding {
constructor(public node: CompileNode, public sourceAst: TemplateAst) {}
}

View File

@ -1,427 +0,0 @@
import {BaseException} from '@angular/core';
import {isPresent, isBlank} from '../../src/facade/lang';
import {ListWrapper, StringMapWrapper} from '../../src/facade/collection';
import * as o from '../output/output_ast';
import {Identifiers, identifierToken} from '../identifiers';
import {InjectMethodVars} from './constants';
import {CompileView} from './compile_view';
import {TemplateAst, ProviderAst, ProviderAstType, ReferenceAst} from '../template_ast';
import {
CompileTokenMap,
CompileDirectiveMetadata,
CompileTokenMetadata,
CompileQueryMetadata,
CompileProviderMetadata,
CompileDiDependencyMetadata,
CompileIdentifierMetadata,
} from '../compile_metadata';
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
import {CompileMethod} from './compile_method';
import {ValueTransformer, visitValue} from '../util';
export class CompileNode {
constructor(public parent: CompileElement, public view: CompileView, public nodeIndex: number,
public renderNode: o.Expression, public sourceAst: TemplateAst) {}
isNull(): boolean { return isBlank(this.renderNode); }
isRootElement(): boolean { return this.view != this.parent.view; }
}
export class CompileElement extends CompileNode {
static createNull(): CompileElement {
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
}
private _compViewExpr: o.Expression = null;
public appElement: o.ReadPropExpr;
public elementRef: o.Expression;
public injector: o.Expression;
private _instances = new CompileTokenMap<o.Expression>();
private _resolvedProviders: CompileTokenMap<ProviderAst>;
private _queryCount = 0;
private _queries = new CompileTokenMap<CompileQuery[]>();
private _componentConstructorViewQueryLists: o.Expression[] = [];
public contentNodesByNgContentIndex: Array<o.Expression>[] = null;
public embeddedView: CompileView;
public directiveInstances: o.Expression[];
public referenceTokens: {[key: string]: CompileTokenMetadata};
constructor(parent: CompileElement, view: CompileView, nodeIndex: number,
renderNode: o.Expression, sourceAst: TemplateAst,
public component: CompileDirectiveMetadata,
private _directives: CompileDirectiveMetadata[],
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
super(parent, view, nodeIndex, renderNode, sourceAst);
this.referenceTokens = {};
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
this.elementRef = o.importExpr(Identifiers.ElementRef).instantiate([this.renderNode]);
this._instances.add(identifierToken(Identifiers.ElementRef), this.elementRef);
this.injector = o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]);
this._instances.add(identifierToken(Identifiers.Injector), this.injector);
this._instances.add(identifierToken(Identifiers.Renderer), o.THIS_EXPR.prop('renderer'));
if (this.hasViewContainer || this.hasEmbeddedView || isPresent(this.component)) {
this._createAppElement();
}
}
private _createAppElement() {
var fieldName = `_appEl_${this.nodeIndex}`;
var parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
// private is fine here as no child view will reference an AppElement
this.view.fields.push(new o.ClassField(fieldName, o.importType(Identifiers.AppElement),
[o.StmtModifier.Private]));
var statement = o.THIS_EXPR.prop(fieldName)
.set(o.importExpr(Identifiers.AppElement)
.instantiate([
o.literal(this.nodeIndex),
o.literal(parentNodeIndex),
o.THIS_EXPR,
this.renderNode
]))
.toStmt();
this.view.createMethod.addStmt(statement);
this.appElement = o.THIS_EXPR.prop(fieldName);
this._instances.add(identifierToken(Identifiers.AppElement), this.appElement);
}
setComponentView(compViewExpr: o.Expression) {
this._compViewExpr = compViewExpr;
this.contentNodesByNgContentIndex =
ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length);
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
this.contentNodesByNgContentIndex[i] = [];
}
}
setEmbeddedView(embeddedView: CompileView) {
this.embeddedView = embeddedView;
if (isPresent(embeddedView)) {
var createTemplateRefExpr =
o.importExpr(Identifiers.TemplateRef_)
.instantiate([this.appElement, this.embeddedView.viewFactory]);
var provider = new CompileProviderMetadata(
{token: identifierToken(Identifiers.TemplateRef), useValue: createTemplateRefExpr});
// Add TemplateRef as first provider as it does not have deps on other providers
this._resolvedProvidersArray.unshift(new ProviderAst(provider.token, false, true, [provider],
ProviderAstType.Builtin,
this.sourceAst.sourceSpan));
}
}
beforeChildren(): void {
if (this.hasViewContainer) {
this._instances.add(identifierToken(Identifiers.ViewContainerRef),
this.appElement.prop('vcRef'));
}
this._resolvedProviders = new CompileTokenMap<ProviderAst>();
this._resolvedProvidersArray.forEach(provider =>
this._resolvedProviders.add(provider.token, provider));
// create all the provider instances, some in the view constructor,
// some as getters. We rely on the fact that they are already sorted topologically.
this._resolvedProviders.values().forEach((resolvedProvider) => {
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
if (isPresent(provider.useExisting)) {
return this._getDependency(
resolvedProvider.providerType,
new CompileDiDependencyMetadata({token: provider.useExisting}));
} else if (isPresent(provider.useFactory)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
return o.importExpr(provider.useFactory).callFn(depsExpr);
} else if (isPresent(provider.useClass)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass));
} else {
return _convertValueToOutputAst(provider.useValue);
}
});
var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`;
var instance =
createProviderProperty(propName, resolvedProvider, providerValueExpressions,
resolvedProvider.multiProvider, resolvedProvider.eager, this);
this._instances.add(resolvedProvider.token, instance);
});
this.directiveInstances =
this._directives.map((directive) => this._instances.get(identifierToken(directive.type)));
for (var i = 0; i < this.directiveInstances.length; i++) {
var directiveInstance = this.directiveInstances[i];
var directive = this._directives[i];
directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); });
}
var queriesWithReads: _QueryWithRead[] = [];
this._resolvedProviders.values().forEach((resolvedProvider) => {
var queriesForProvider = this._getQueriesFor(resolvedProvider.token);
ListWrapper.addAll(
queriesWithReads,
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
});
StringMapWrapper.forEach(this.referenceTokens, (_, varName) => {
var token = this.referenceTokens[varName];
var varValue;
if (isPresent(token)) {
varValue = this._instances.get(token);
} else {
varValue = this.renderNode;
}
this.view.locals.set(varName, varValue);
var varToken = new CompileTokenMetadata({value: varName});
ListWrapper.addAll(queriesWithReads, this._getQueriesFor(varToken)
.map(query => new _QueryWithRead(query, varToken)));
});
queriesWithReads.forEach((queryWithRead) => {
var value: o.Expression;
if (isPresent(queryWithRead.read.identifier)) {
// query for an identifier
value = this._instances.get(queryWithRead.read);
} else {
// query for a reference
var token = this.referenceTokens[queryWithRead.read.value];
if (isPresent(token)) {
value = this._instances.get(token);
} else {
value = this.elementRef;
}
}
if (isPresent(value)) {
queryWithRead.query.addValue(value, this.view);
}
});
if (isPresent(this.component)) {
var componentConstructorViewQueryList =
isPresent(this.component) ? o.literalArr(this._componentConstructorViewQueryLists) :
o.NULL_EXPR;
var compExpr = isPresent(this.getComponent()) ? this.getComponent() : o.NULL_EXPR;
this.view.createMethod.addStmt(
this.appElement.callMethod(
'initComponent',
[compExpr, componentConstructorViewQueryList, this._compViewExpr])
.toStmt());
}
}
afterChildren(childNodeCount: number) {
this._resolvedProviders.values().forEach((resolvedProvider) => {
// Note: afterChildren is called after recursing into children.
// This is good so that an injector match in an element that is closer to a requesting element
// matches first.
var providerExpr = this._instances.get(resolvedProvider.token);
// Note: view providers are only visible on the injector of that element.
// This is not fully correct as the rules during codegen don't allow a directive
// to get hold of a view provdier on the same element. We still do this semantic
// as it simplifies our model to having only one runtime injector per element.
var providerChildNodeCount =
resolvedProvider.providerType === ProviderAstType.PrivateService ? 0 : childNodeCount;
this.view.injectorGetMethod.addStmt(createInjectInternalCondition(
this.nodeIndex, providerChildNodeCount, resolvedProvider, providerExpr));
});
this._queries.values().forEach(
(queries) =>
queries.forEach((query) => query.afterChildren(this.view.updateContentQueriesMethod)));
}
addContentNode(ngContentIndex: number, nodeExpr: o.Expression) {
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
}
getComponent(): o.Expression {
return isPresent(this.component) ? this._instances.get(identifierToken(this.component.type)) :
null;
}
getProviderTokens(): o.Expression[] {
return this._resolvedProviders.values().map(
(resolvedProvider) => createDiTokenExpression(resolvedProvider.token));
}
private _getQueriesFor(token: CompileTokenMetadata): CompileQuery[] {
var result: CompileQuery[] = [];
var currentEl: CompileElement = this;
var distance = 0;
var queries: CompileQuery[];
while (!currentEl.isNull()) {
queries = currentEl._queries.get(token);
if (isPresent(queries)) {
ListWrapper.addAll(result,
queries.filter((query) => query.meta.descendants || distance <= 1));
}
if (currentEl._directives.length > 0) {
distance++;
}
currentEl = currentEl.parent;
}
queries = this.view.componentView.viewQueries.get(token);
if (isPresent(queries)) {
ListWrapper.addAll(result, queries);
}
return result;
}
private _addQuery(queryMeta: CompileQueryMetadata,
directiveInstance: o.Expression): CompileQuery {
var propName = `_query_${queryMeta.selectors[0].name}_${this.nodeIndex}_${this._queryCount++}`;
var queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
addQueryToTokenMap(this._queries, query);
return query;
}
private _getLocalDependency(requestingProviderType: ProviderAstType,
dep: CompileDiDependencyMetadata): o.Expression {
var result = null;
// constructor content query
if (isBlank(result) && isPresent(dep.query)) {
result = this._addQuery(dep.query, null).queryList;
}
// constructor view query
if (isBlank(result) && isPresent(dep.viewQuery)) {
result = createQueryList(
dep.viewQuery, null,
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
this.view);
this._componentConstructorViewQueryLists.push(result);
}
if (isPresent(dep.token)) {
// access builtins with special visibility
if (isBlank(result)) {
if (dep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef))) {
if (requestingProviderType === ProviderAstType.Component) {
return this._compViewExpr.prop('ref');
} else {
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
}
}
}
// access regular providers on the element
if (isBlank(result)) {
result = this._instances.get(dep.token);
}
}
return result;
}
private _getDependency(requestingProviderType: ProviderAstType,
dep: CompileDiDependencyMetadata): o.Expression {
var currElement: CompileElement = this;
var result = null;
if (dep.isValue) {
result = o.literal(dep.value);
}
if (isBlank(result) && !dep.isSkipSelf) {
result = this._getLocalDependency(requestingProviderType, dep);
}
// check parent elements
while (isBlank(result) && !currElement.parent.isNull()) {
currElement = currElement.parent;
result = currElement._getLocalDependency(ProviderAstType.PublicService,
new CompileDiDependencyMetadata({token: dep.token}));
}
if (isBlank(result)) {
result = injectFromViewParentInjector(dep.token, dep.isOptional);
}
if (isBlank(result)) {
result = o.NULL_EXPR;
}
return getPropertyInView(result, this.view, currElement.view);
}
}
function createInjectInternalCondition(nodeIndex: number, childNodeCount: number,
provider: ProviderAst,
providerExpr: o.Expression): o.Statement {
var indexCondition;
if (childNodeCount > 0) {
indexCondition = o.literal(nodeIndex)
.lowerEquals(InjectMethodVars.requestNodeIndex)
.and(InjectMethodVars.requestNodeIndex.lowerEquals(
o.literal(nodeIndex + childNodeCount)));
} else {
indexCondition = o.literal(nodeIndex).identical(InjectMethodVars.requestNodeIndex);
}
return new o.IfStmt(
InjectMethodVars.token.identical(createDiTokenExpression(provider.token)).and(indexCondition),
[new o.ReturnStatement(providerExpr)]);
}
function createProviderProperty(propName: string, provider: ProviderAst,
providerValueExpressions: o.Expression[], isMulti: boolean,
isEager: boolean, compileElement: CompileElement): o.Expression {
var view = compileElement.view;
var resolvedProviderValueExpr;
var type;
if (isMulti) {
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
type = new o.ArrayType(o.DYNAMIC_TYPE);
} else {
resolvedProviderValueExpr = providerValueExpressions[0];
type = providerValueExpressions[0].type;
}
if (isBlank(type)) {
type = o.DYNAMIC_TYPE;
}
if (isEager) {
view.fields.push(new o.ClassField(propName, type));
view.createMethod.addStmt(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
} else {
var internalField = `_${propName}`;
view.fields.push(new o.ClassField(internalField, type));
var getter = new CompileMethod(view);
getter.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
// Note: Equals is important for JS so that it also checks the undefined case!
getter.addStmt(
new o.IfStmt(o.THIS_EXPR.prop(internalField).isBlank(),
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]));
getter.addStmt(new o.ReturnStatement(o.THIS_EXPR.prop(internalField)));
view.getters.push(new o.ClassGetter(propName, getter.finish(), type));
}
return o.THIS_EXPR.prop(propName);
}
class _QueryWithRead {
public read: CompileTokenMetadata;
constructor(public query: CompileQuery, match: CompileTokenMetadata) {
this.read = isPresent(query.meta.read) ? query.meta.read : match;
}
}
function _convertValueToOutputAst(value: any): o.Expression {
return visitValue(value, new _ValueOutputAstTransformer(), null);
}
class _ValueOutputAstTransformer extends ValueTransformer {
visitArray(arr: any[], context: any): o.Expression {
return o.literalArr(arr.map(value => visitValue(value, this, context)));
}
visitStringMap(map: {[key: string]: any}, context: any): o.Expression {
var entries = [];
StringMapWrapper.forEach(
map, (value, key) => { entries.push([key, visitValue(value, this, context)]); });
return o.literalMap(entries);
}
visitPrimitive(value: any, context: any): o.Expression { return o.literal(value); }
visitOther(value: any, context: any): o.Expression {
if (value instanceof CompileIdentifierMetadata) {
return o.importExpr(value);
} else if (value instanceof o.Expression) {
return value;
} else {
throw new BaseException(`Illegal state: Don't now how to compile value ${value}`);
}
}
}

View File

@ -1,75 +0,0 @@
import {isPresent} from '../../src/facade/lang';
import {ListWrapper} from '../../src/facade/collection';
import * as o from '../output/output_ast';
import {TemplateAst} from '../template_ast';
import {CompileView} from './compile_view';
class _DebugState {
constructor(public nodeIndex: number, public sourceAst: TemplateAst) {}
}
var NULL_DEBUG_STATE = new _DebugState(null, null);
export class CompileMethod {
private _newState: _DebugState = NULL_DEBUG_STATE;
private _currState: _DebugState = NULL_DEBUG_STATE;
private _debugEnabled: boolean;
private _bodyStatements: o.Statement[] = [];
constructor(private _view: CompileView) {
this._debugEnabled = this._view.genConfig.genDebugInfo;
}
private _updateDebugContextIfNeeded() {
if (this._newState.nodeIndex !== this._currState.nodeIndex ||
this._newState.sourceAst !== this._currState.sourceAst) {
var expr = this._updateDebugContext(this._newState);
if (isPresent(expr)) {
this._bodyStatements.push(expr.toStmt());
}
}
}
private _updateDebugContext(newState: _DebugState): o.Expression {
this._currState = this._newState = newState;
if (this._debugEnabled) {
var sourceLocation =
isPresent(newState.sourceAst) ? newState.sourceAst.sourceSpan.start : null;
return o.THIS_EXPR.callMethod('debug', [
o.literal(newState.nodeIndex),
isPresent(sourceLocation) ? o.literal(sourceLocation.line) : o.NULL_EXPR,
isPresent(sourceLocation) ? o.literal(sourceLocation.col) : o.NULL_EXPR
]);
} else {
return null;
}
}
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
var res = this._updateDebugContext(new _DebugState(nodeIndex, templateAst));
return isPresent(res) ? res : o.NULL_EXPR;
}
resetDebugInfo(nodeIndex: number, templateAst: TemplateAst) {
this._newState = new _DebugState(nodeIndex, templateAst);
}
addStmt(stmt: o.Statement) {
this._updateDebugContextIfNeeded();
this._bodyStatements.push(stmt);
}
addStmts(stmts: o.Statement[]) {
this._updateDebugContextIfNeeded();
ListWrapper.addAll(this._bodyStatements, stmts);
}
finish(): o.Statement[] { return this._bodyStatements; }
isEmpty(): boolean { return this._bodyStatements.length === 0; }
}

View File

@ -1,97 +0,0 @@
import {isBlank, isPresent} from '../facade/lang';
import {BaseException} from '../facade/exceptions';
import * as o from '../output/output_ast';
import {CompileView} from './compile_view';
import {CompilePipeMetadata} from '../compile_metadata';
import {Identifiers, identifierToken} from '../identifiers';
import {injectFromViewParentInjector, createPureProxy, getPropertyInView} from './util';
class _PurePipeProxy {
constructor(public view: CompileView, public instance: o.ReadPropExpr, public argCount: number) {}
}
export class CompilePipe {
static call(view: CompileView, name: string, args: o.Expression[]): o.Expression {
var compView = view.componentView;
var meta = _findPipeMeta(compView, name);
var pipe: CompilePipe;
if (meta.pure) {
// pure pipes live on the component view
pipe = compView.purePipes.get(name);
if (isBlank(pipe)) {
pipe = new CompilePipe(compView, meta);
compView.purePipes.set(name, pipe);
compView.pipes.push(pipe);
}
} else {
// Non pure pipes live on the view that called it
pipe = new CompilePipe(view, meta);
view.pipes.push(pipe);
}
return pipe._call(view, args);
}
instance: o.ReadPropExpr;
private _purePipeProxies: _PurePipeProxy[] = [];
constructor(public view: CompileView, public meta: CompilePipeMetadata) {
this.instance = o.THIS_EXPR.prop(`_pipe_${meta.name}_${view.pipeCount++}`);
}
get pure(): boolean { return this.meta.pure; }
create(): void {
var deps = this.meta.type.diDeps.map((diDep) => {
if (diDep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef))) {
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
}
return injectFromViewParentInjector(diDep.token, false);
});
this.view.fields.push(new o.ClassField(this.instance.name, o.importType(this.meta.type)));
this.view.createMethod.resetDebugInfo(null, null);
this.view.createMethod.addStmt(o.THIS_EXPR.prop(this.instance.name)
.set(o.importExpr(this.meta.type).instantiate(deps))
.toStmt());
this._purePipeProxies.forEach((purePipeProxy) => {
var pipeInstanceSeenFromPureProxy =
getPropertyInView(this.instance, purePipeProxy.view, this.view);
createPureProxy(pipeInstanceSeenFromPureProxy.prop('transform')
.callMethod(o.BuiltinMethod.bind, [pipeInstanceSeenFromPureProxy]),
purePipeProxy.argCount, purePipeProxy.instance, purePipeProxy.view);
});
}
private _call(callingView: CompileView, args: o.Expression[]): o.Expression {
if (this.meta.pure) {
// PurePipeProxies live on the view that called them.
var purePipeProxy = new _PurePipeProxy(
callingView, o.THIS_EXPR.prop(`${this.instance.name}_${this._purePipeProxies.length}`),
args.length);
this._purePipeProxies.push(purePipeProxy);
return o.importExpr(Identifiers.castByValue)
.callFn([
purePipeProxy.instance,
getPropertyInView(this.instance.prop('transform'), callingView, this.view)
])
.callFn(args);
} else {
return getPropertyInView(this.instance, callingView, this.view).callMethod('transform', args);
}
}
}
function _findPipeMeta(view: CompileView, name: string): CompilePipeMetadata {
var pipeMeta: CompilePipeMetadata = null;
for (var i = view.pipeMetas.length - 1; i >= 0; i--) {
var localPipeMeta = view.pipeMetas[i];
if (localPipeMeta.name == name) {
pipeMeta = localPipeMeta;
break;
}
}
if (isBlank(pipeMeta)) {
throw new BaseException(
`Illegal state: Could not find pipe ${name} although the parser should have detected this error!`);
}
return pipeMeta;
}

View File

@ -1,113 +0,0 @@
import {isPresent, isBlank} from '../../src/facade/lang';
import {ListWrapper} from '../../src/facade/collection';
import * as o from '../output/output_ast';
import {Identifiers} from '../identifiers';
import {CompileQueryMetadata, CompileTokenMap} from '../compile_metadata';
import {CompileView} from './compile_view';
import {CompileElement} from './compile_element';
import {CompileMethod} from './compile_method';
import {getPropertyInView} from './util';
class ViewQueryValues {
constructor(public view: CompileView, public values: Array<o.Expression | ViewQueryValues>) {}
}
export class CompileQuery {
private _values: ViewQueryValues;
constructor(public meta: CompileQueryMetadata, public queryList: o.Expression,
public ownerDirectiveExpression: o.Expression, public view: CompileView) {
this._values = new ViewQueryValues(view, []);
}
addValue(value: o.Expression, view: CompileView) {
var currentView = view;
var elPath: CompileElement[] = [];
while (isPresent(currentView) && currentView !== this.view) {
var parentEl = currentView.declarationElement;
elPath.unshift(parentEl);
currentView = parentEl.view;
}
var queryListForDirtyExpr = getPropertyInView(this.queryList, view, this.view);
var viewValues = this._values;
elPath.forEach((el) => {
var last =
viewValues.values.length > 0 ? viewValues.values[viewValues.values.length - 1] : null;
if (last instanceof ViewQueryValues && last.view === el.embeddedView) {
viewValues = last;
} else {
var newViewValues = new ViewQueryValues(el.embeddedView, []);
viewValues.values.push(newViewValues);
viewValues = newViewValues;
}
});
viewValues.values.push(value);
if (elPath.length > 0) {
view.dirtyParentQueriesMethod.addStmt(
queryListForDirtyExpr.callMethod('setDirty', []).toStmt());
}
}
afterChildren(targetMethod: CompileMethod) {
var values = createQueryValues(this._values);
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
if (isPresent(this.ownerDirectiveExpression)) {
var valueExpr = this.meta.first ? this.queryList.prop('first') : this.queryList;
updateStmts.push(
this.ownerDirectiveExpression.prop(this.meta.propertyName).set(valueExpr).toStmt());
}
if (!this.meta.first) {
updateStmts.push(this.queryList.callMethod('notifyOnChanges', []).toStmt());
}
targetMethod.addStmt(new o.IfStmt(this.queryList.prop('dirty'), updateStmts));
}
}
function createQueryValues(viewValues: ViewQueryValues): o.Expression[] {
return ListWrapper.flatten(viewValues.values.map((entry) => {
if (entry instanceof ViewQueryValues) {
return mapNestedViews(entry.view.declarationElement.appElement, entry.view,
createQueryValues(entry));
} else {
return <o.Expression>entry;
}
}));
}
function mapNestedViews(declarationAppElement: o.Expression, view: CompileView,
expressions: o.Expression[]): o.Expression {
var adjustedExpressions: o.Expression[] = expressions.map((expr) => {
return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr);
});
return declarationAppElement.callMethod('mapNestedViews', [
o.variable(view.className),
o.fn([new o.FnParam('nestedView', view.classType)],
[new o.ReturnStatement(o.literalArr(adjustedExpressions))])
]);
}
export function createQueryList(query: CompileQueryMetadata, directiveInstance: o.Expression,
propertyName: string, compileView: CompileView): o.Expression {
compileView.fields.push(new o.ClassField(propertyName, o.importType(Identifiers.QueryList)));
var expr = o.THIS_EXPR.prop(propertyName);
compileView.createMethod.addStmt(o.THIS_EXPR.prop(propertyName)
.set(o.importExpr(Identifiers.QueryList).instantiate([]))
.toStmt());
return expr;
}
export function addQueryToTokenMap(map: CompileTokenMap<CompileQuery[]>, query: CompileQuery) {
query.meta.selectors.forEach((selector) => {
var entry = map.get(selector);
if (isBlank(entry)) {
entry = [];
map.add(selector, entry);
}
entry.push(query);
});
}

View File

@ -1,201 +0,0 @@
import {ViewType} from '../../core_private';
import {isPresent, isBlank} from '../../src/facade/lang';
import {ListWrapper} from '../../src/facade/collection';
import * as o from '../output/output_ast';
import {EventHandlerVars} from './constants';
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
import {NameResolver} from './expression_converter';
import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method';
import {CompilePipe} from './compile_pipe';
import {
CompileDirectiveMetadata,
CompilePipeMetadata,
CompileIdentifierMetadata,
CompileTokenMap
} from '../compile_metadata';
import {
getViewFactoryName,
injectFromViewParentInjector,
createDiTokenExpression,
getPropertyInView,
createPureProxy
} from './util';
import {CompilerConfig} from '../config';
import {CompileBinding} from './compile_binding';
import {Identifiers} from '../identifiers';
export class CompileView implements NameResolver {
public viewType: ViewType;
public viewQueries: CompileTokenMap<CompileQuery[]>;
public nodes: CompileNode[] = [];
// root nodes or AppElements for ViewContainers
public rootNodesOrAppElements: o.Expression[] = [];
public bindings: CompileBinding[] = [];
public classStatements: o.Statement[] = [];
public createMethod: CompileMethod;
public injectorGetMethod: CompileMethod;
public updateContentQueriesMethod: CompileMethod;
public dirtyParentQueriesMethod: CompileMethod;
public updateViewQueriesMethod: CompileMethod;
public detectChangesInInputsMethod: CompileMethod;
public detectChangesRenderPropertiesMethod: CompileMethod;
public afterContentLifecycleCallbacksMethod: CompileMethod;
public afterViewLifecycleCallbacksMethod: CompileMethod;
public destroyMethod: CompileMethod;
public eventHandlerMethods: o.ClassMethod[] = [];
public fields: o.ClassField[] = [];
public getters: o.ClassGetter[] = [];
public disposables: o.Expression[] = [];
public subscriptions: o.Expression[] = [];
public componentView: CompileView;
public purePipes = new Map<string, CompilePipe>();
public pipes: CompilePipe[] = [];
public locals = new Map<string, o.Expression>();
public className: string;
public classType: o.Type;
public viewFactory: o.ReadVarExpr;
public literalArrayCount = 0;
public literalMapCount = 0;
public pipeCount = 0;
public componentContext: o.Expression;
constructor(public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
public viewIndex: number, public declarationElement: CompileElement,
public templateVariableBindings: string[][]) {
this.createMethod = new CompileMethod(this);
this.injectorGetMethod = new CompileMethod(this);
this.updateContentQueriesMethod = new CompileMethod(this);
this.dirtyParentQueriesMethod = new CompileMethod(this);
this.updateViewQueriesMethod = new CompileMethod(this);
this.detectChangesInInputsMethod = new CompileMethod(this);
this.detectChangesRenderPropertiesMethod = new CompileMethod(this);
this.afterContentLifecycleCallbacksMethod = new CompileMethod(this);
this.afterViewLifecycleCallbacksMethod = new CompileMethod(this);
this.destroyMethod = new CompileMethod(this);
this.viewType = getViewType(component, viewIndex);
this.className = `_View_${component.type.name}${viewIndex}`;
this.classType = o.importType(new CompileIdentifierMetadata({name: this.className}));
this.viewFactory = o.variable(getViewFactoryName(component, viewIndex));
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
this.componentView = this;
} else {
this.componentView = this.declarationElement.view.componentView;
}
this.componentContext =
getPropertyInView(o.THIS_EXPR.prop('context'), this, this.componentView);
var viewQueries = new CompileTokenMap<CompileQuery[]>();
if (this.viewType === ViewType.COMPONENT) {
var directiveInstance = o.THIS_EXPR.prop('context');
ListWrapper.forEachWithIndex(this.component.viewQueries, (queryMeta, queryIndex) => {
var propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`;
var queryList = createQueryList(queryMeta, directiveInstance, propName, this);
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
addQueryToTokenMap(viewQueries, query);
});
var constructorViewQueryCount = 0;
this.component.type.diDeps.forEach((dep) => {
if (isPresent(dep.viewQuery)) {
var queryList = o.THIS_EXPR.prop('declarationAppElement')
.prop('componentConstructorViewQueries')
.key(o.literal(constructorViewQueryCount++));
var query = new CompileQuery(dep.viewQuery, queryList, null, this);
addQueryToTokenMap(viewQueries, query);
}
});
}
this.viewQueries = viewQueries;
templateVariableBindings.forEach(
(entry) => { this.locals.set(entry[1], o.THIS_EXPR.prop('context').prop(entry[0])); });
if (!this.declarationElement.isNull()) {
this.declarationElement.setEmbeddedView(this);
}
}
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression {
return CompilePipe.call(this, name, [input].concat(args));
}
getLocal(name: string): o.Expression {
if (name == EventHandlerVars.event.name) {
return EventHandlerVars.event;
}
var currView: CompileView = this;
var result = currView.locals.get(name);
while (isBlank(result) && isPresent(currView.declarationElement.view)) {
currView = currView.declarationElement.view;
result = currView.locals.get(name);
}
if (isPresent(result)) {
return getPropertyInView(result, this, currView);
} else {
return null;
}
}
createLiteralArray(values: o.Expression[]): o.Expression {
if (values.length === 0) {
return o.importExpr(Identifiers.EMPTY_ARRAY);
}
var proxyExpr = o.THIS_EXPR.prop(`_arr_${this.literalArrayCount++}`);
var proxyParams: o.FnParam[] = [];
var proxyReturnEntries: o.Expression[] = [];
for (var i = 0; i < values.length; i++) {
var paramName = `p${i}`;
proxyParams.push(new o.FnParam(paramName));
proxyReturnEntries.push(o.variable(paramName));
}
createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))]),
values.length, proxyExpr, this);
return proxyExpr.callFn(values);
}
createLiteralMap(entries: Array<Array<string | o.Expression>>): o.Expression {
if (entries.length === 0) {
return o.importExpr(Identifiers.EMPTY_MAP);
}
var proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
var proxyParams: o.FnParam[] = [];
var proxyReturnEntries: Array<Array<string | o.Expression>> = [];
var values: o.Expression[] = [];
for (var i = 0; i < entries.length; i++) {
var paramName = `p${i}`;
proxyParams.push(new o.FnParam(paramName));
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
values.push(<o.Expression>entries[i][1]);
}
createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))]),
entries.length, proxyExpr, this);
return proxyExpr.callFn(values);
}
afterNodes() {
this.pipes.forEach((pipe) => pipe.create());
this.viewQueries.values().forEach(
(queries) => queries.forEach((query) => query.afterChildren(this.updateViewQueriesMethod)));
}
}
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
if (embeddedTemplateIndex > 0) {
return ViewType.EMBEDDED;
} else if (component.type.isHost) {
return ViewType.HOST;
} else {
return ViewType.COMPONENT;
}
}

View File

@ -1,83 +0,0 @@
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {ChangeDetectorState, ViewType} from '../../core_private';
import {isBlank, resolveEnumToken} from '../../src/facade/lang';
import {CompileIdentifierMetadata} from '../compile_metadata';
import * as o from '../output/output_ast';
import {Identifiers} from '../identifiers';
function _enumExpression(classIdentifier: CompileIdentifierMetadata, value: any): o.Expression {
if (isBlank(value)) return o.NULL_EXPR;
var name = resolveEnumToken(classIdentifier.runtime, value);
return o.importExpr(new CompileIdentifierMetadata({
name: `${classIdentifier.name}.${name}`,
moduleUrl: classIdentifier.moduleUrl,
runtime: value
}));
}
export class ViewTypeEnum {
static fromValue(value: ViewType): o.Expression {
return _enumExpression(Identifiers.ViewType, value);
}
static HOST = ViewTypeEnum.fromValue(ViewType.HOST);
static COMPONENT = ViewTypeEnum.fromValue(ViewType.COMPONENT);
static EMBEDDED = ViewTypeEnum.fromValue(ViewType.EMBEDDED);
}
export class ViewEncapsulationEnum {
static fromValue(value: ViewEncapsulation): o.Expression {
return _enumExpression(Identifiers.ViewEncapsulation, value);
}
static Emulated = ViewEncapsulationEnum.fromValue(ViewEncapsulation.Emulated);
static Native = ViewEncapsulationEnum.fromValue(ViewEncapsulation.Native);
static None = ViewEncapsulationEnum.fromValue(ViewEncapsulation.None);
}
export class ChangeDetectorStateEnum {
static fromValue(value: ChangeDetectorState): o.Expression {
return _enumExpression(Identifiers.ChangeDetectorState, value);
}
static NeverChecked = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.NeverChecked);
static CheckedBefore = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.CheckedBefore);
static Errored = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.Errored);
}
export class ChangeDetectionStrategyEnum {
static fromValue(value: ChangeDetectionStrategy): o.Expression {
return _enumExpression(Identifiers.ChangeDetectionStrategy, value);
}
static CheckOnce = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.CheckOnce);
static Checked = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Checked);
static CheckAlways = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.CheckAlways);
static Detached = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Detached);
static OnPush = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.OnPush);
static Default = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Default);
}
export class ViewConstructorVars {
static viewUtils = o.variable('viewUtils');
static parentInjector = o.variable('parentInjector');
static declarationEl = o.variable('declarationEl');
}
export class ViewProperties {
static renderer = o.THIS_EXPR.prop('renderer');
static projectableNodes = o.THIS_EXPR.prop('projectableNodes');
static viewUtils = o.THIS_EXPR.prop('viewUtils');
}
export class EventHandlerVars { static event = o.variable('$event'); }
export class InjectMethodVars {
static token = o.variable('token');
static requestNodeIndex = o.variable('requestNodeIndex');
static notFoundResult = o.variable('notFoundResult');
}
export class DetectChangesVars {
static throwOnChange = o.variable(`throwOnChange`);
static changes = o.variable(`changes`);
static changed = o.variable(`changed`);
static valUnwrapper = o.variable(`valUnwrapper`);
}

View File

@ -1,164 +0,0 @@
import {isBlank, isPresent, StringWrapper} from '../../src/facade/lang';
import {ListWrapper, StringMapWrapper} from '../../src/facade/collection';
import {EventHandlerVars, ViewProperties} from './constants';
import * as o from '../output/output_ast';
import {CompileElement} from './compile_element';
import {CompileMethod} from './compile_method';
import {BoundEventAst, DirectiveAst} from '../template_ast';
import {CompileDirectiveMetadata} from '../compile_metadata';
import {convertCdStatementToIr} from './expression_converter';
import {CompileBinding} from './compile_binding';
export class CompileEventListener {
private _method: CompileMethod;
private _hasComponentHostListener: boolean = false;
private _methodName: string;
private _eventParam: o.FnParam;
private _actionResultExprs: o.Expression[] = [];
static getOrCreate(compileElement: CompileElement, eventTarget: string, eventName: string,
targetEventListeners: CompileEventListener[]): CompileEventListener {
var listener = targetEventListeners.find(listener => listener.eventTarget == eventTarget &&
listener.eventName == eventName);
if (isBlank(listener)) {
listener = new CompileEventListener(compileElement, eventTarget, eventName,
targetEventListeners.length);
targetEventListeners.push(listener);
}
return listener;
}
constructor(public compileElement: CompileElement, public eventTarget: string,
public eventName: string, listenerIndex: number) {
this._method = new CompileMethod(compileElement.view);
this._methodName =
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
this._eventParam =
new o.FnParam(EventHandlerVars.event.name,
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
}
addAction(hostEvent: BoundEventAst, directive: CompileDirectiveMetadata,
directiveInstance: o.Expression) {
if (isPresent(directive) && directive.isComponent) {
this._hasComponentHostListener = true;
}
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
var context = isPresent(directiveInstance) ? directiveInstance :
this.compileElement.view.componentContext;
var actionStmts = convertCdStatementToIr(this.compileElement.view, context, hostEvent.handler);
var lastIndex = actionStmts.length - 1;
if (lastIndex >= 0) {
var lastStatement = actionStmts[lastIndex];
var returnExpr = convertStmtIntoExpression(lastStatement);
var preventDefaultVar = o.variable(`pd_${this._actionResultExprs.length}`);
this._actionResultExprs.push(preventDefaultVar);
if (isPresent(returnExpr)) {
// Note: We need to cast the result of the method call to dynamic,
// as it might be a void method!
actionStmts[lastIndex] =
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
.toDeclStmt(null, [o.StmtModifier.Final]);
}
}
this._method.addStmts(actionStmts);
}
finishMethod() {
var markPathToRootStart = this._hasComponentHostListener ?
this.compileElement.appElement.prop('componentView') :
o.THIS_EXPR;
var resultExpr: o.Expression = o.literal(true);
this._actionResultExprs.forEach((expr) => { resultExpr = resultExpr.and(expr); });
var stmts =
(<o.Statement[]>[markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt()])
.concat(this._method.finish())
.concat([new o.ReturnStatement(resultExpr)]);
// private is fine here as no child view will reference the event handler...
this.compileElement.view.eventHandlerMethods.push(new o.ClassMethod(
this._methodName, [this._eventParam], stmts, o.BOOL_TYPE, [o.StmtModifier.Private]));
}
listenToRenderer() {
var listenExpr;
var eventListener = o.THIS_EXPR.callMethod(
'eventHandler',
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.bind, [o.THIS_EXPR])]);
if (isPresent(this.eventTarget)) {
listenExpr = ViewProperties.renderer.callMethod(
'listenGlobal', [o.literal(this.eventTarget), o.literal(this.eventName), eventListener]);
} else {
listenExpr = ViewProperties.renderer.callMethod(
'listen', [this.compileElement.renderNode, o.literal(this.eventName), eventListener]);
}
var disposable = o.variable(`disposable_${this.compileElement.view.disposables.length}`);
this.compileElement.view.disposables.push(disposable);
// private is fine here as no child view will reference the event handler...
this.compileElement.view.createMethod.addStmt(
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
}
listenToDirective(directiveInstance: o.Expression, observablePropName: string) {
var subscription = o.variable(`subscription_${this.compileElement.view.subscriptions.length}`);
this.compileElement.view.subscriptions.push(subscription);
var eventListener = o.THIS_EXPR.callMethod(
'eventHandler',
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.bind, [o.THIS_EXPR])]);
this.compileElement.view.createMethod.addStmt(
subscription.set(directiveInstance.prop(observablePropName)
.callMethod(o.BuiltinMethod.SubscribeObservable, [eventListener]))
.toDeclStmt(null, [o.StmtModifier.Final]));
}
}
export function collectEventListeners(hostEvents: BoundEventAst[], dirs: DirectiveAst[],
compileElement: CompileElement): CompileEventListener[] {
var eventListeners: CompileEventListener[] = [];
hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate(compileElement, hostEvent.target,
hostEvent.name, eventListeners);
listener.addAction(hostEvent, null, null);
});
ListWrapper.forEachWithIndex(dirs, (directiveAst, i) => {
var directiveInstance = compileElement.directiveInstances[i];
directiveAst.hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate(compileElement, hostEvent.target,
hostEvent.name, eventListeners);
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
});
});
eventListeners.forEach((listener) => listener.finishMethod());
return eventListeners;
}
export function bindDirectiveOutputs(directiveAst: DirectiveAst, directiveInstance: o.Expression,
eventListeners: CompileEventListener[]) {
StringMapWrapper.forEach(directiveAst.directive.outputs, (eventName, observablePropName) => {
eventListeners.filter(listener => listener.eventName == eventName)
.forEach(
(listener) => { listener.listenToDirective(directiveInstance, observablePropName); });
});
}
export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
eventListeners.forEach(listener => listener.listenToRenderer());
}
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
if (stmt instanceof o.ExpressionStatement) {
return stmt.expr;
} else if (stmt instanceof o.ReturnStatement) {
return stmt.value;
}
return null;
}
function santitizeEventName(name: string): string {
return StringWrapper.replaceAll(name, /[^a-zA-Z_]/g, '_');
}

View File

@ -1,252 +0,0 @@
import {BaseException} from '../../src/facade/exceptions';
import {isBlank, isPresent, isArray} from '../../src/facade/lang';
import * as cdAst from '../expression_parser/ast';
import * as o from '../output/output_ast';
import {Identifiers} from '../identifiers';
var IMPLICIT_RECEIVER = o.variable('#implicit');
export interface NameResolver {
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
getLocal(name: string): o.Expression;
createLiteralArray(values: o.Expression[]): o.Expression;
createLiteralMap(values: Array<Array<string | o.Expression>>): o.Expression;
}
export class ExpressionWithWrappedValueInfo {
constructor(public expression: o.Expression, public needsValueUnwrapper: boolean) {}
}
export function convertCdExpressionToIr(
nameResolver: NameResolver, implicitReceiver: o.Expression, expression: cdAst.AST,
valueUnwrapper: o.ReadVarExpr): ExpressionWithWrappedValueInfo {
var visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, valueUnwrapper);
var irAst: o.Expression = expression.visit(visitor, _Mode.Expression);
return new ExpressionWithWrappedValueInfo(irAst, visitor.needsValueUnwrapper);
}
export function convertCdStatementToIr(nameResolver: NameResolver, implicitReceiver: o.Expression,
stmt: cdAst.AST): o.Statement[] {
var visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, null);
var statements = [];
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements);
return statements;
}
enum _Mode {
Statement,
Expression
}
function ensureStatementMode(mode: _Mode, ast: cdAst.AST) {
if (mode !== _Mode.Statement) {
throw new BaseException(`Expected a statement, but saw ${ast}`);
}
}
function ensureExpressionMode(mode: _Mode, ast: cdAst.AST) {
if (mode !== _Mode.Expression) {
throw new BaseException(`Expected an expression, but saw ${ast}`);
}
}
function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expression | o.Statement {
if (mode === _Mode.Statement) {
return expr.toStmt();
} else {
return expr;
}
}
class _AstToIrVisitor implements cdAst.AstVisitor {
public needsValueUnwrapper: boolean = false;
constructor(private _nameResolver: NameResolver, private _implicitReceiver: o.Expression,
private _valueUnwrapper: o.ReadVarExpr) {}
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
var op;
switch (ast.operation) {
case '+':
op = o.BinaryOperator.Plus;
break;
case '-':
op = o.BinaryOperator.Minus;
break;
case '*':
op = o.BinaryOperator.Multiply;
break;
case '/':
op = o.BinaryOperator.Divide;
break;
case '%':
op = o.BinaryOperator.Modulo;
break;
case '&&':
op = o.BinaryOperator.And;
break;
case '||':
op = o.BinaryOperator.Or;
break;
case '==':
op = o.BinaryOperator.Equals;
break;
case '!=':
op = o.BinaryOperator.NotEquals;
break;
case '===':
op = o.BinaryOperator.Identical;
break;
case '!==':
op = o.BinaryOperator.NotIdentical;
break;
case '<':
op = o.BinaryOperator.Lower;
break;
case '>':
op = o.BinaryOperator.Bigger;
break;
case '<=':
op = o.BinaryOperator.LowerEquals;
break;
case '>=':
op = o.BinaryOperator.BiggerEquals;
break;
default:
throw new BaseException(`Unsupported operation ${ast.operation}`);
}
return convertToStatementIfNeeded(
mode, new o.BinaryOperatorExpr(op, ast.left.visit(this, _Mode.Expression),
ast.right.visit(this, _Mode.Expression)));
}
visitChain(ast: cdAst.Chain, mode: _Mode): any {
ensureStatementMode(mode, ast);
return this.visitAll(ast.expressions, mode);
}
visitConditional(ast: cdAst.Conditional, mode: _Mode): any {
var value: o.Expression = ast.condition.visit(this, _Mode.Expression);
return convertToStatementIfNeeded(
mode, value.conditional(ast.trueExp.visit(this, _Mode.Expression),
ast.falseExp.visit(this, _Mode.Expression)));
}
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
var input = ast.exp.visit(this, _Mode.Expression);
var args = this.visitAll(ast.args, _Mode.Expression);
var value = this._nameResolver.callPipe(ast.name, input, args);
this.needsValueUnwrapper = true;
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
}
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
return convertToStatementIfNeeded(mode, ast.target.visit(this, _Mode.Expression)
.callFn(this.visitAll(ast.args, _Mode.Expression)));
}
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
ensureExpressionMode(mode, ast);
return IMPLICIT_RECEIVER;
}
visitInterpolation(ast: cdAst.Interpolation, mode: _Mode): any {
ensureExpressionMode(mode, ast);
var args = [o.literal(ast.expressions.length)];
for (var i = 0; i < ast.strings.length - 1; i++) {
args.push(o.literal(ast.strings[i]));
args.push(ast.expressions[i].visit(this, _Mode.Expression));
}
args.push(o.literal(ast.strings[ast.strings.length - 1]));
return o.importExpr(Identifiers.interpolate).callFn(args);
}
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
return convertToStatementIfNeeded(
mode, ast.obj.visit(this, _Mode.Expression).key(ast.key.visit(this, _Mode.Expression)));
}
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
var obj: o.Expression = ast.obj.visit(this, _Mode.Expression);
var key: o.Expression = ast.key.visit(this, _Mode.Expression);
var value: o.Expression = ast.value.visit(this, _Mode.Expression);
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
}
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
return convertToStatementIfNeeded(
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
}
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
var parts = [];
for (var i = 0; i < ast.keys.length; i++) {
parts.push([ast.keys[i], ast.values[i].visit(this, _Mode.Expression)]);
}
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
}
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
return convertToStatementIfNeeded(mode, o.literal(ast.value));
}
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
var args = this.visitAll(ast.args, _Mode.Expression);
var result = null;
var receiver = ast.receiver.visit(this, _Mode.Expression);
if (receiver === IMPLICIT_RECEIVER) {
var varExpr = this._nameResolver.getLocal(ast.name);
if (isPresent(varExpr)) {
result = varExpr.callFn(args);
} else {
receiver = this._implicitReceiver;
}
}
if (isBlank(result)) {
result = receiver.callMethod(ast.name, args);
}
return convertToStatementIfNeeded(mode, result);
}
visitPrefixNot(ast: cdAst.PrefixNot, mode: _Mode): any {
return convertToStatementIfNeeded(mode, o.not(ast.expression.visit(this, _Mode.Expression)));
}
visitPropertyRead(ast: cdAst.PropertyRead, mode: _Mode): any {
var result = null;
var receiver = ast.receiver.visit(this, _Mode.Expression);
if (receiver === IMPLICIT_RECEIVER) {
result = this._nameResolver.getLocal(ast.name);
if (isBlank(result)) {
receiver = this._implicitReceiver;
}
}
if (isBlank(result)) {
result = receiver.prop(ast.name);
}
return convertToStatementIfNeeded(mode, result);
}
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
var receiver: o.Expression = ast.receiver.visit(this, _Mode.Expression);
if (receiver === IMPLICIT_RECEIVER) {
var varExpr = this._nameResolver.getLocal(ast.name);
if (isPresent(varExpr)) {
throw new BaseException('Cannot assign to a reference or variable!');
}
receiver = this._implicitReceiver;
}
return convertToStatementIfNeeded(
mode, receiver.prop(ast.name).set(ast.value.visit(this, _Mode.Expression)));
}
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
var receiver = ast.receiver.visit(this, _Mode.Expression);
return convertToStatementIfNeeded(
mode, receiver.isBlank().conditional(o.NULL_EXPR, receiver.prop(ast.name)));
}
visitSafeMethodCall(ast: cdAst.SafeMethodCall, mode: _Mode): any {
var receiver = ast.receiver.visit(this, _Mode.Expression);
var args = this.visitAll(ast.args, _Mode.Expression);
return convertToStatementIfNeeded(
mode, receiver.isBlank().conditional(o.NULL_EXPR, receiver.callMethod(ast.name, args)));
}
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => ast.visit(this, mode)); }
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
throw new BaseException('Quotes are not supported for evaluation!');
}
}
function flattenStatements(arg: any, output: o.Statement[]) {
if (isArray(arg)) {
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
} else {
output.push(arg);
}
}

View File

@ -1,88 +0,0 @@
import {LifecycleHooks} from '../../core_private';
import * as o from '../output/output_ast';
import {DetectChangesVars, ChangeDetectorStateEnum} from './constants';
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
import {DirectiveAst} from '../template_ast';
import {CompileElement} from './compile_element';
import {CompileView} from './compile_view';
var STATE_IS_NEVER_CHECKED =
o.THIS_EXPR.prop('cdState').identical(ChangeDetectorStateEnum.NeverChecked);
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
export function bindDirectiveDetectChangesLifecycleCallbacks(
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
var lifecycleHooks = directiveAst.directive.lifecycleHooks;
if (lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 && directiveAst.inputs.length > 0) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
DetectChangesVars.changes.notIdentical(o.NULL_EXPR),
[directiveInstance.callMethod('ngOnChanges', [DetectChangesVars.changes]).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1) {
detectChangesInInputsMethod.addStmt(
new o.IfStmt(STATE_IS_NEVER_CHECKED.and(NOT_THROW_ON_CHANGES),
[directiveInstance.callMethod('ngOnInit', []).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
NOT_THROW_ON_CHANGES, [directiveInstance.callMethod('ngDoCheck', []).toStmt()]));
}
}
export function bindDirectiveAfterContentLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
directiveInstance: o.Expression,
compileElement: CompileElement) {
var view = compileElement.view;
var lifecycleHooks = directiveMeta.lifecycleHooks;
var afterContentLifecycleCallbacksMethod = view.afterContentLifecycleCallbacksMethod;
afterContentLifecycleCallbacksMethod.resetDebugInfo(compileElement.nodeIndex,
compileElement.sourceAst);
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1) {
afterContentLifecycleCallbacksMethod.addStmt(new o.IfStmt(
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterContentInit', []).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1) {
afterContentLifecycleCallbacksMethod.addStmt(
directiveInstance.callMethod('ngAfterContentChecked', []).toStmt());
}
}
export function bindDirectiveAfterViewLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
directiveInstance: o.Expression,
compileElement: CompileElement) {
var view = compileElement.view;
var lifecycleHooks = directiveMeta.lifecycleHooks;
var afterViewLifecycleCallbacksMethod = view.afterViewLifecycleCallbacksMethod;
afterViewLifecycleCallbacksMethod.resetDebugInfo(compileElement.nodeIndex,
compileElement.sourceAst);
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1) {
afterViewLifecycleCallbacksMethod.addStmt(new o.IfStmt(
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterViewInit', []).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1) {
afterViewLifecycleCallbacksMethod.addStmt(
directiveInstance.callMethod('ngAfterViewChecked', []).toStmt());
}
}
export function bindDirectiveDestroyLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
directiveInstance: o.Expression,
compileElement: CompileElement) {
var onDestroyMethod = compileElement.view.destroyMethod;
onDestroyMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
if (directiveMeta.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
onDestroyMethod.addStmt(directiveInstance.callMethod('ngOnDestroy', []).toStmt());
}
}
export function bindPipeDestroyLifecycleCallbacks(pipeMeta: CompilePipeMetadata,
pipeInstance: o.Expression, view: CompileView) {
var onDestroyMethod = view.destroyMethod;
if (pipeMeta.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
onDestroyMethod.addStmt(pipeInstance.callMethod('ngOnDestroy', []).toStmt());
}
}

View File

@ -1,239 +0,0 @@
import {SecurityContext} from '../../core_private';
import {LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
import {isBlank, isPresent} from '../../src/facade/lang';
import * as cdAst from '../expression_parser/ast';
import * as o from '../output/output_ast';
import {Identifiers} from '../identifiers';
import {DetectChangesVars, ViewProperties} from './constants';
import {
BoundTextAst,
BoundElementPropertyAst,
DirectiveAst,
PropertyBindingType,
} from '../template_ast';
import {CompileView} from './compile_view';
import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method';
import {camelCaseToDashCase} from '../util';
import {convertCdExpressionToIr} from './expression_converter';
import {CompileBinding} from './compile_binding';
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
return o.THIS_EXPR.prop(`_expr_${exprIndex}`);
}
function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
}
function bind(view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
method: CompileMethod) {
var checkExpression =
convertCdExpressionToIr(view, context, parsedExpression, DetectChangesVars.valUnwrapper);
if (isBlank(checkExpression.expression)) {
// e.g. an empty expression was given
return;
}
// private is fine here as no child view will reference the cached value...
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
view.createMethod.addStmt(
o.THIS_EXPR.prop(fieldExpr.name).set(o.importExpr(Identifiers.uninitialized)).toStmt());
if (checkExpression.needsValueUnwrapper) {
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
method.addStmt(initValueUnwrapperStmt);
}
method.addStmt(
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
var condition: o.Expression =
o.importExpr(Identifiers.checkBinding)
.callFn([DetectChangesVars.throwOnChange, fieldExpr, currValExpr]);
if (checkExpression.needsValueUnwrapper) {
condition = DetectChangesVars.valUnwrapper.prop('hasWrappedValue').or(condition);
}
method.addStmt(new o.IfStmt(
condition,
actions.concat([<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(currValExpr).toStmt()])));
}
export function bindRenderText(boundText: BoundTextAst, compileNode: CompileNode,
view: CompileView) {
var bindingIndex = view.bindings.length;
view.bindings.push(new CompileBinding(compileNode, boundText));
var currValExpr = createCurrValueExpr(bindingIndex);
var valueField = createBindFieldExpr(bindingIndex);
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
bind(view, currValExpr, valueField, boundText.value, view.componentContext,
[
o.THIS_EXPR.prop('renderer')
.callMethod('setText', [compileNode.renderNode, currValExpr])
.toStmt()
],
view.detectChangesRenderPropertiesMethod);
}
function bindAndWriteToRenderer(boundProps: BoundElementPropertyAst[], context: o.Expression,
compileElement: CompileElement) {
var view = compileElement.view;
var renderNode = compileElement.renderNode;
boundProps.forEach((boundProp) => {
var bindingIndex = view.bindings.length;
view.bindings.push(new CompileBinding(compileElement, boundProp));
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
var fieldExpr = createBindFieldExpr(bindingIndex);
var currValExpr = createCurrValueExpr(bindingIndex);
var renderMethod: string;
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
var updateStmts = [];
switch (boundProp.type) {
case PropertyBindingType.Property:
renderMethod = 'setElementProperty';
if (view.genConfig.logBindingUpdate) {
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, currValExpr));
}
break;
case PropertyBindingType.Attribute:
renderMethod = 'setElementAttribute';
renderValue =
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
break;
case PropertyBindingType.Class:
renderMethod = 'setElementClass';
break;
case PropertyBindingType.Style:
renderMethod = 'setElementStyle';
var strValue: o.Expression = renderValue.callMethod('toString', []);
if (isPresent(boundProp.unit)) {
strValue = strValue.plus(o.literal(boundProp.unit));
}
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
break;
}
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod(renderMethod, [renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
bind(view, currValExpr, fieldExpr, boundProp.value, context, updateStmts,
view.detectChangesRenderPropertiesMethod);
});
}
function sanitizedValue(boundProp: BoundElementPropertyAst, renderValue: o.Expression): o.Expression {
let enumValue: string;
switch (boundProp.securityContext) {
case SecurityContext.NONE:
return renderValue; // No sanitization needed.
case SecurityContext.HTML:
enumValue = 'HTML';
break;
case SecurityContext.STYLE:
enumValue = 'STYLE';
break;
case SecurityContext.SCRIPT:
enumValue = 'SCRIPT';
break;
case SecurityContext.URL:
enumValue = 'URL';
break;
case SecurityContext.RESOURCE_URL:
enumValue = 'RESOURCE_URL';
break;
default:
throw new Error(`internal error, unexpected SecurityContext ${boundProp.securityContext}.`);
}
let ctx = ViewProperties.viewUtils.prop('sanitizer');
let args = [o.importExpr(Identifiers.SecurityContext).prop(enumValue), renderValue];
return ctx.callMethod('sanitize', args);
}
export function bindRenderInputs(boundProps: BoundElementPropertyAst[],
compileElement: CompileElement): void {
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement);
}
export function bindDirectiveHostProps(directiveAst: DirectiveAst, directiveInstance: o.Expression,
compileElement: CompileElement): void {
bindAndWriteToRenderer(directiveAst.hostProperties, directiveInstance, compileElement);
}
export function bindDirectiveInputs(directiveAst: DirectiveAst, directiveInstance: o.Expression,
compileElement: CompileElement) {
if (directiveAst.inputs.length === 0) {
return;
}
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
var lifecycleHooks = directiveAst.directive.lifecycleHooks;
var calcChangesMap = lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
if (calcChangesMap) {
detectChangesInInputsMethod.addStmt(DetectChangesVars.changes.set(o.NULL_EXPR).toStmt());
}
if (isOnPushComp) {
detectChangesInInputsMethod.addStmt(DetectChangesVars.changed.set(o.literal(false)).toStmt());
}
directiveAst.inputs.forEach((input) => {
var bindingIndex = view.bindings.length;
view.bindings.push(new CompileBinding(compileElement, input));
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
var fieldExpr = createBindFieldExpr(bindingIndex);
var currValExpr = createCurrValueExpr(bindingIndex);
var statements: o.Statement[] =
[directiveInstance.prop(input.directiveName).set(currValExpr).toStmt()];
if (calcChangesMap) {
statements.push(new o.IfStmt(DetectChangesVars.changes.identical(o.NULL_EXPR), [
DetectChangesVars.changes.set(o.literalMap([], new o.MapType(
o.importType(Identifiers.SimpleChange))))
.toStmt()
]));
statements.push(
DetectChangesVars.changes.key(o.literal(input.directiveName))
.set(o.importExpr(Identifiers.SimpleChange).instantiate([fieldExpr, currValExpr]))
.toStmt());
}
if (isOnPushComp) {
statements.push(DetectChangesVars.changed.set(o.literal(true)).toStmt());
}
if (view.genConfig.logBindingUpdate) {
statements.push(
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
}
bind(view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
detectChangesInInputsMethod);
});
if (isOnPushComp) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(DetectChangesVars.changed, [
compileElement.appElement.prop('componentView')
.callMethod('markAsCheckOnce', [])
.toStmt()
]));
}
}
function logBindingUpdateStmt(renderNode: o.Expression, propName: string,
value: o.Expression): o.Statement {
return o.THIS_EXPR.prop('renderer')
.callMethod('setBindingDebugInfo',
[
renderNode,
o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
value.isBlank().conditional(o.NULL_EXPR, value.callMethod('toString', []))
])
.toStmt();
}

View File

@ -1,98 +0,0 @@
import {isPresent, isBlank} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import * as o from '../output/output_ast';
import {
CompileTokenMetadata,
CompileDirectiveMetadata,
} from '../compile_metadata';
import {CompileView} from './compile_view';
import {Identifiers} from '../identifiers';
export function getPropertyInView(property: o.Expression, callingView: CompileView,
definedView: CompileView): o.Expression {
if (callingView === definedView) {
return property;
} else {
var viewProp: o.Expression = o.THIS_EXPR;
var currView: CompileView = callingView;
while (currView !== definedView && isPresent(currView.declarationElement.view)) {
currView = currView.declarationElement.view;
viewProp = viewProp.prop('parent');
}
if (currView !== definedView) {
throw new BaseException(
`Internal error: Could not calculate a property in a parent view: ${property}`);
}
if (property instanceof o.ReadPropExpr) {
let readPropExpr: o.ReadPropExpr = property;
// Note: Don't cast for members of the AppView base class...
if (definedView.fields.some((field) => field.name == readPropExpr.name) ||
definedView.getters.some((field) => field.name == readPropExpr.name)) {
viewProp = viewProp.cast(definedView.classType);
}
}
return o.replaceVarInExpression(o.THIS_EXPR.name, viewProp, property);
}
}
export function injectFromViewParentInjector(token: CompileTokenMetadata,
optional: boolean): o.Expression {
var args = [createDiTokenExpression(token)];
if (optional) {
args.push(o.NULL_EXPR);
}
return o.THIS_EXPR.prop('parentInjector').callMethod('get', args);
}
export function getViewFactoryName(component: CompileDirectiveMetadata,
embeddedTemplateIndex: number): string {
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
}
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
if (isPresent(token.value)) {
return o.literal(token.value);
} else if (token.identifierIsInstance) {
return o.importExpr(token.identifier)
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
} else {
return o.importExpr(token.identifier);
}
}
export function createFlatArray(expressions: o.Expression[]): o.Expression {
var lastNonArrayExpressions = [];
var result: o.Expression = o.literalArr([]);
for (var i = 0; i < expressions.length; i++) {
var expr = expressions[i];
if (expr.type instanceof o.ArrayType) {
if (lastNonArrayExpressions.length > 0) {
result =
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
lastNonArrayExpressions = [];
}
result = result.callMethod(o.BuiltinMethod.ConcatArray, [expr]);
} else {
lastNonArrayExpressions.push(expr);
}
}
if (lastNonArrayExpressions.length > 0) {
result =
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
}
return result;
}
export function createPureProxy(fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
view: CompileView) {
view.fields.push(new o.ClassField(pureProxyProp.name, null));
var pureProxyId =
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
if (isBlank(pureProxyId)) {
throw new BaseException(`Unsupported number of argument for pure functions: ${argCount}`);
}
view.createMethod.addStmt(
o.THIS_EXPR.prop(pureProxyProp.name).set(o.importExpr(pureProxyId).callFn([fn])).toStmt());
}

View File

@ -1,119 +0,0 @@
import {
ListWrapper,
} from '../../src/facade/collection';
import {
TemplateAst,
TemplateAstVisitor,
NgContentAst,
EmbeddedTemplateAst,
ElementAst,
ReferenceAst,
VariableAst,
BoundEventAst,
BoundElementPropertyAst,
AttrAst,
BoundTextAst,
TextAst,
DirectiveAst,
BoundDirectivePropertyAst,
templateVisitAll,
} from '../template_ast';
import {
bindRenderText,
bindRenderInputs,
bindDirectiveInputs,
bindDirectiveHostProps
} from './property_binder';
import {bindRenderOutputs, collectEventListeners, bindDirectiveOutputs} from './event_binder';
import {
bindDirectiveAfterContentLifecycleCallbacks,
bindDirectiveAfterViewLifecycleCallbacks,
bindDirectiveDestroyLifecycleCallbacks,
bindPipeDestroyLifecycleCallbacks,
bindDirectiveDetectChangesLifecycleCallbacks
} from './lifecycle_binder';
import {CompileView} from './compile_view';
import {CompileElement, CompileNode} from './compile_element';
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
var visitor = new ViewBinderVisitor(view);
templateVisitAll(visitor, parsedTemplate);
view.pipes.forEach(
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
}
class ViewBinderVisitor implements TemplateAstVisitor {
private _nodeIndex: number = 0;
constructor(public view: CompileView) {}
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
var node = this.view.nodes[this._nodeIndex++];
bindRenderText(ast, node, this.view);
return null;
}
visitText(ast: TextAst, parent: CompileElement): any {
this._nodeIndex++;
return null;
}
visitNgContent(ast: NgContentAst, parent: CompileElement): any { return null; }
visitElement(ast: ElementAst, parent: CompileElement): any {
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
bindRenderInputs(ast.inputs, compileElement);
bindRenderOutputs(eventListeners);
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
var directiveInstance = compileElement.directiveInstances[index];
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
});
templateVisitAll(this, ast.children, compileElement);
// afterContent and afterView lifecycles need to be called bottom up
// so that children are notified before parents
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
var directiveInstance = compileElement.directiveInstances[index];
bindDirectiveAfterContentLifecycleCallbacks(directiveAst.directive, directiveInstance,
compileElement);
bindDirectiveAfterViewLifecycleCallbacks(directiveAst.directive, directiveInstance,
compileElement);
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
compileElement);
});
return null;
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
var directiveInstance = compileElement.directiveInstances[index];
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
bindDirectiveAfterContentLifecycleCallbacks(directiveAst.directive, directiveInstance,
compileElement);
bindDirectiveAfterViewLifecycleCallbacks(directiveAst.directive, directiveInstance,
compileElement);
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
compileElement);
});
bindView(compileElement.embeddedView, ast.children);
return null;
}
visitAttr(ast: AttrAst, ctx: any): any { return null; }
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
return null;
}
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
visitVariable(ast: VariableAst, ctx: any): any { return null; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
}

View File

@ -1,576 +0,0 @@
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {isDefaultChangeDetectionStrategy, ViewType} from '../../core_private';
import {isPresent, StringWrapper} from '../../src/facade/lang';
import {ListWrapper, StringMapWrapper, SetWrapper} from '../../src/facade/collection';
import * as o from '../output/output_ast';
import {Identifiers, identifierToken} from '../identifiers';
import {
ViewConstructorVars,
InjectMethodVars,
DetectChangesVars,
ViewTypeEnum,
ViewEncapsulationEnum,
ChangeDetectionStrategyEnum,
ViewProperties
} from './constants';
import {CompileView} from './compile_view';
import {CompileElement, CompileNode} from './compile_element';
import {
TemplateAst,
TemplateAstVisitor,
NgContentAst,
EmbeddedTemplateAst,
ElementAst,
ReferenceAst,
VariableAst,
BoundEventAst,
BoundElementPropertyAst,
AttrAst,
BoundTextAst,
TextAst,
DirectiveAst,
BoundDirectivePropertyAst,
templateVisitAll,
} from '../template_ast';
import {getViewFactoryName, createFlatArray, createDiTokenExpression} from './util';
import {
CompileIdentifierMetadata,
CompileDirectiveMetadata,
CompileTokenMetadata
} from '../compile_metadata';
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
const CLASS_ATTR = 'class';
const STYLE_ATTR = 'style';
var parentRenderNodeVar = o.variable('parentRenderNode');
var rootSelectorVar = o.variable('rootSelector');
export class ViewCompileDependency {
constructor(public comp: CompileDirectiveMetadata,
public factoryPlaceholder: CompileIdentifierMetadata) {}
}
export function buildView(view: CompileView, template: TemplateAst[],
targetDependencies: ViewCompileDependency[]): number {
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
templateVisitAll(builderVisitor, template, view.declarationElement.isNull() ?
view.declarationElement :
view.declarationElement.parent);
return builderVisitor.nestedViewCount;
}
export function finishView(view: CompileView, targetStatements: o.Statement[]) {
view.afterNodes();
createViewTopLevelStmts(view, targetStatements);
view.nodes.forEach((node) => {
if (node instanceof CompileElement && node.hasEmbeddedView) {
finishView(node.embeddedView, targetStatements);
}
});
}
class ViewBuilderVisitor implements TemplateAstVisitor {
nestedViewCount: number = 0;
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[]) {}
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
private _addRootNodeAndProject(node: CompileNode, ngContentIndex: number,
parent: CompileElement) {
var vcAppEl =
(node instanceof CompileElement && node.hasViewContainer) ? node.appElement : null;
if (this._isRootNode(parent)) {
// store appElement as root node only for ViewContainers
if (this.view.viewType !== ViewType.COMPONENT) {
this.view.rootNodesOrAppElements.push(isPresent(vcAppEl) ? vcAppEl : node.renderNode);
}
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
parent.addContentNode(ngContentIndex, isPresent(vcAppEl) ? vcAppEl : node.renderNode);
}
}
private _getParentRenderNode(parent: CompileElement): o.Expression {
if (this._isRootNode(parent)) {
if (this.view.viewType === ViewType.COMPONENT) {
return parentRenderNodeVar;
} else {
// root node of an embedded/host view
return o.NULL_EXPR;
}
} else {
return isPresent(parent.component) &&
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
o.NULL_EXPR :
parent.renderNode;
}
}
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
return this._visitText(ast, '', ast.ngContentIndex, parent);
}
visitText(ast: TextAst, parent: CompileElement): any {
return this._visitText(ast, ast.value, ast.ngContentIndex, parent);
}
private _visitText(ast: TemplateAst, value: string, ngContentIndex: number,
parent: CompileElement): o.Expression {
var fieldName = `_text_${this.view.nodes.length}`;
this.view.fields.push(
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderText)));
var renderNode = o.THIS_EXPR.prop(fieldName);
var compileNode = new CompileNode(parent, this.view, this.view.nodes.length, renderNode, ast);
var createRenderNode =
o.THIS_EXPR.prop(fieldName)
.set(ViewProperties.renderer.callMethod(
'createText',
[
this._getParentRenderNode(parent),
o.literal(value),
this.view.createMethod.resetDebugInfoExpr(this.view.nodes.length, ast)
]))
.toStmt();
this.view.nodes.push(compileNode);
this.view.createMethod.addStmt(createRenderNode);
this._addRootNodeAndProject(compileNode, ngContentIndex, parent);
return renderNode;
}
visitNgContent(ast: NgContentAst, parent: CompileElement): any {
// the projected nodes originate from a different view, so we don't
// have debug information for them...
this.view.createMethod.resetDebugInfo(null, ast);
var parentRenderNode = this._getParentRenderNode(parent);
var nodesExpression = ViewProperties.projectableNodes.key(
o.literal(ast.index),
new o.ArrayType(o.importType(this.view.genConfig.renderTypes.renderNode)));
if (parentRenderNode !== o.NULL_EXPR) {
this.view.createMethod.addStmt(
ViewProperties.renderer.callMethod(
'projectNodes',
[
parentRenderNode,
o.importExpr(Identifiers.flattenNestedViewRenderNodes)
.callFn([nodesExpression])
])
.toStmt());
} else if (this._isRootNode(parent)) {
if (this.view.viewType !== ViewType.COMPONENT) {
// store root nodes only for embedded/host views
this.view.rootNodesOrAppElements.push(nodesExpression);
}
} else {
if (isPresent(parent.component) && isPresent(ast.ngContentIndex)) {
parent.addContentNode(ast.ngContentIndex, nodesExpression);
}
}
return null;
}
visitElement(ast: ElementAst, parent: CompileElement): any {
var nodeIndex = this.view.nodes.length;
var createRenderNodeExpr;
var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
createRenderNodeExpr = o.THIS_EXPR.callMethod(
'selectOrCreateHostElement', [o.literal(ast.name), rootSelectorVar, debugContextExpr]);
} else {
createRenderNodeExpr = ViewProperties.renderer.callMethod(
'createElement',
[this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]);
}
var fieldName = `_el_${nodeIndex}`;
this.view.fields.push(
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderElement)));
this.view.createMethod.addStmt(o.THIS_EXPR.prop(fieldName).set(createRenderNodeExpr).toStmt());
var renderNode = o.THIS_EXPR.prop(fieldName);
var directives = ast.directives.map(directiveAst => directiveAst.directive);
var component = directives.find(directive => directive.isComponent);
var htmlAttrs = _readHtmlAttrs(ast.attrs);
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
for (var i = 0; i < attrNameAndValues.length; i++) {
var attrName = attrNameAndValues[i][0];
var attrValue = attrNameAndValues[i][1];
this.view.createMethod.addStmt(
ViewProperties.renderer.callMethod(
'setElementAttribute',
[renderNode, o.literal(attrName), o.literal(attrValue)])
.toStmt());
}
var compileElement =
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, component, directives,
ast.providers, ast.hasViewContainer, false, ast.references);
this.view.nodes.push(compileElement);
var compViewExpr: o.ReadVarExpr = null;
if (isPresent(component)) {
var nestedComponentIdentifier =
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
this.targetDependencies.push(new ViewCompileDependency(component, nestedComponentIdentifier));
compViewExpr = o.variable(`compView_${nodeIndex}`); // fix highlighting: `
compileElement.setComponentView(compViewExpr);
this.view.createMethod.addStmt(compViewExpr.set(o.importExpr(nestedComponentIdentifier)
.callFn([
ViewProperties.viewUtils,
compileElement.injector,
compileElement.appElement
]))
.toDeclStmt());
}
compileElement.beforeChildren();
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
templateVisitAll(this, ast.children, compileElement);
compileElement.afterChildren(this.view.nodes.length - nodeIndex - 1);
if (isPresent(compViewExpr)) {
var codeGenContentNodes;
if (this.view.component.type.isHost) {
codeGenContentNodes = ViewProperties.projectableNodes;
} else {
codeGenContentNodes = o.literalArr(
compileElement.contentNodesByNgContentIndex.map(nodes => createFlatArray(nodes)));
}
this.view.createMethod.addStmt(
compViewExpr.callMethod('create',
[compileElement.getComponent(), codeGenContentNodes, o.NULL_EXPR])
.toStmt());
}
return null;
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
var nodeIndex = this.view.nodes.length;
var fieldName = `_anchor_${nodeIndex}`;
this.view.fields.push(
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderComment)));
this.view.createMethod.addStmt(
o.THIS_EXPR.prop(fieldName)
.set(ViewProperties.renderer.callMethod(
'createTemplateAnchor',
[
this._getParentRenderNode(parent),
this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast)
]))
.toStmt());
var renderNode = o.THIS_EXPR.prop(fieldName);
var templateVariableBindings = ast.variables.map(
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
var directives = ast.directives.map(directiveAst => directiveAst.directive);
var compileElement =
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, null, directives,
ast.providers, ast.hasViewContainer, true, ast.references);
this.view.nodes.push(compileElement);
this.nestedViewCount++;
var embeddedView = new CompileView(
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
compileElement.beforeChildren();
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
compileElement.afterChildren(0);
return null;
}
visitAttr(ast: AttrAst, ctx: any): any { return null; }
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
return null;
}
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
visitVariable(ast: VariableAst, ctx: any): any { return null; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
}
function _mergeHtmlAndDirectiveAttrs(declaredHtmlAttrs: {[key: string]: string},
directives: CompileDirectiveMetadata[]): string[][] {
var result: {[key: string]: string} = {};
StringMapWrapper.forEach(declaredHtmlAttrs, (value, key) => { result[key] = value; });
directives.forEach(directiveMeta => {
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
var prevValue = result[name];
result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
});
});
return mapToKeyValueArray(result);
}
function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
var htmlAttrs: {[key: string]: string} = {};
attrs.forEach((ast) => { htmlAttrs[ast.name] = ast.value; });
return htmlAttrs;
}
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
return `${attrValue1} ${attrValue2}`;
} else {
return attrValue2;
}
}
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
var entryArray = [];
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
// We need to sort to get a defined output order
// for tests and for caching generated artifacts...
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
var keyValueArray = [];
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
return keyValueArray;
}
function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statement[]) {
var nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
if (view.genConfig.genDebugInfo) {
nodeDebugInfosVar = o.variable(
`nodeDebugInfos_${view.component.type.name}${view.viewIndex}`); // fix highlighting: `
targetStatements.push(
(<o.ReadVarExpr>nodeDebugInfosVar)
.set(o.literalArr(view.nodes.map(createStaticNodeDebugInfo),
new o.ArrayType(new o.ExternalType(Identifiers.StaticNodeDebugInfo),
[o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]));
}
var renderCompTypeVar: o.ReadVarExpr =
o.variable(`renderType_${view.component.type.name}`); // fix highlighting: `
if (view.viewIndex === 0) {
targetStatements.push(renderCompTypeVar.set(o.NULL_EXPR)
.toDeclStmt(o.importType(Identifiers.RenderComponentType)));
}
var viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar);
targetStatements.push(viewClass);
targetStatements.push(createViewFactory(view, viewClass, renderCompTypeVar));
}
function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
var compileElement = node instanceof CompileElement ? node : null;
var providerTokens: o.Expression[] = [];
var componentToken: o.Expression = o.NULL_EXPR;
var varTokenEntries = [];
if (isPresent(compileElement)) {
providerTokens = compileElement.getProviderTokens();
if (isPresent(compileElement.component)) {
componentToken = createDiTokenExpression(identifierToken(compileElement.component.type));
}
StringMapWrapper.forEach(compileElement.referenceTokens, (token, varName) => {
varTokenEntries.push(
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
});
}
return o.importExpr(Identifiers.StaticNodeDebugInfo)
.instantiate(
[
o.literalArr(providerTokens, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])),
componentToken,
o.literalMap(varTokenEntries, new o.MapType(o.DYNAMIC_TYPE, [o.TypeModifier.Const]))
],
o.importType(Identifiers.StaticNodeDebugInfo, null, [o.TypeModifier.Const]));
}
function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
nodeDebugInfosVar: o.Expression): o.ClassStmt {
var viewConstructorArgs = [
new o.FnParam(ViewConstructorVars.viewUtils.name, o.importType(Identifiers.ViewUtils)),
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
];
var superConstructorArgs = [
o.variable(view.className),
renderCompTypeVar,
ViewTypeEnum.fromValue(view.viewType),
ViewConstructorVars.viewUtils,
ViewConstructorVars.parentInjector,
ViewConstructorVars.declarationEl,
ChangeDetectionStrategyEnum.fromValue(getChangeDetectionMode(view))
];
if (view.genConfig.genDebugInfo) {
superConstructorArgs.push(nodeDebugInfosVar);
}
var viewConstructor = new o.ClassMethod(null, viewConstructorArgs,
[o.SUPER_EXPR.callFn(superConstructorArgs).toStmt()]);
var viewMethods = [
new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
generateCreateMethod(view), o.importType(Identifiers.AppElement)),
new o.ClassMethod(
'injectorGetInternal',
[
new o.FnParam(InjectMethodVars.token.name, o.DYNAMIC_TYPE),
// Note: Can't use o.INT_TYPE here as the method in AppView uses number
new o.FnParam(InjectMethodVars.requestNodeIndex.name, o.NUMBER_TYPE),
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
],
addReturnValuefNotEmpty(view.injectorGetMethod.finish(), InjectMethodVars.notFoundResult),
o.DYNAMIC_TYPE),
new o.ClassMethod('detectChangesInternal',
[new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
generateDetectChangesMethod(view)),
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
].concat(view.eventHandlerMethods);
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
var viewClass = new o.ClassStmt(view.className, o.importExpr(superClass, [getContextType(view)]),
view.fields, view.getters, viewConstructor,
viewMethods.filter((method) => method.body.length > 0));
return viewClass;
}
function createViewFactory(view: CompileView, viewClass: o.ClassStmt,
renderCompTypeVar: o.ReadVarExpr): o.Statement {
var viewFactoryArgs = [
new o.FnParam(ViewConstructorVars.viewUtils.name, o.importType(Identifiers.ViewUtils)),
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
];
var initRenderCompTypeStmts = [];
var templateUrlInfo;
if (view.component.template.templateUrl == view.component.type.moduleUrl) {
templateUrlInfo =
`${view.component.type.moduleUrl} class ${view.component.type.name} - inline template`;
} else {
templateUrlInfo = view.component.template.templateUrl;
}
if (view.viewIndex === 0) {
initRenderCompTypeStmts = [
new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR),
[
renderCompTypeVar.set(ViewConstructorVars
.viewUtils.callMethod('createRenderComponentType',
[
o.literal(templateUrlInfo),
o.literal(view.component
.template.ngContentSelectors.length),
ViewEncapsulationEnum
.fromValue(view.component.template.encapsulation),
view.styles
]))
.toStmt()
])
];
}
return o.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([
new o.ReturnStatement(o.variable(viewClass.name)
.instantiate(viewClass.constructorMethod.params.map(
(param) => o.variable(param.name))))
]),
o.importType(Identifiers.AppView, [getContextType(view)]))
.toDeclStmt(view.viewFactory.name, [o.StmtModifier.Final]);
}
function generateCreateMethod(view: CompileView): o.Statement[] {
var parentRenderNodeExpr: o.Expression = o.NULL_EXPR;
var parentRenderNodeStmts = [];
if (view.viewType === ViewType.COMPONENT) {
parentRenderNodeExpr = ViewProperties.renderer.callMethod(
'createViewRoot', [o.THIS_EXPR.prop('declarationAppElement').prop('nativeElement')]);
parentRenderNodeStmts = [
parentRenderNodeVar.set(parentRenderNodeExpr)
.toDeclStmt(o.importType(view.genConfig.renderTypes.renderNode), [o.StmtModifier.Final])
];
}
var resultExpr: o.Expression;
if (view.viewType === ViewType.HOST) {
resultExpr = (<CompileElement>view.nodes[0]).appElement;
} else {
resultExpr = o.NULL_EXPR;
}
return parentRenderNodeStmts.concat(view.createMethod.finish())
.concat([
o.THIS_EXPR.callMethod('init',
[
createFlatArray(view.rootNodesOrAppElements),
o.literalArr(view.nodes.map(node => node.renderNode)),
o.literalArr(view.disposables),
o.literalArr(view.subscriptions)
])
.toStmt(),
new o.ReturnStatement(resultExpr)
]);
}
function generateDetectChangesMethod(view: CompileView): o.Statement[] {
var stmts = [];
if (view.detectChangesInInputsMethod.isEmpty() && view.updateContentQueriesMethod.isEmpty() &&
view.afterContentLifecycleCallbacksMethod.isEmpty() &&
view.detectChangesRenderPropertiesMethod.isEmpty() &&
view.updateViewQueriesMethod.isEmpty() && view.afterViewLifecycleCallbacksMethod.isEmpty()) {
return stmts;
}
ListWrapper.addAll(stmts, view.detectChangesInInputsMethod.finish());
stmts.push(
o.THIS_EXPR.callMethod('detectContentChildrenChanges', [DetectChangesVars.throwOnChange])
.toStmt());
var afterContentStmts = view.updateContentQueriesMethod.finish().concat(
view.afterContentLifecycleCallbacksMethod.finish());
if (afterContentStmts.length > 0) {
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterContentStmts));
}
ListWrapper.addAll(stmts, view.detectChangesRenderPropertiesMethod.finish());
stmts.push(o.THIS_EXPR.callMethod('detectViewChildrenChanges', [DetectChangesVars.throwOnChange])
.toStmt());
var afterViewStmts =
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
if (afterViewStmts.length > 0) {
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterViewStmts));
}
var varStmts = [];
var readVars = o.findReadVarNames(stmts);
if (SetWrapper.has(readVars, DetectChangesVars.changed.name)) {
varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
}
if (SetWrapper.has(readVars, DetectChangesVars.changes.name)) {
varStmts.push(DetectChangesVars.changes.set(o.NULL_EXPR)
.toDeclStmt(new o.MapType(o.importType(Identifiers.SimpleChange))));
}
if (SetWrapper.has(readVars, DetectChangesVars.valUnwrapper.name)) {
varStmts.push(
DetectChangesVars.valUnwrapper.set(o.importExpr(Identifiers.ValueUnwrapper).instantiate([]))
.toDeclStmt(null, [o.StmtModifier.Final]));
}
return varStmts.concat(stmts);
}
function addReturnValuefNotEmpty(statements: o.Statement[], value: o.Expression): o.Statement[] {
if (statements.length > 0) {
return statements.concat([new o.ReturnStatement(value)]);
} else {
return statements;
}
}
function getContextType(view: CompileView): o.Type {
if (view.viewType === ViewType.COMPONENT) {
return o.importType(view.component.type);
}
return o.DYNAMIC_TYPE;
}
function getChangeDetectionMode(view: CompileView): ChangeDetectionStrategy {
var mode: ChangeDetectionStrategy;
if (view.viewType === ViewType.COMPONENT) {
mode = isDefaultChangeDetectionStrategy(view.component.changeDetection) ?
ChangeDetectionStrategy.CheckAlways :
ChangeDetectionStrategy.CheckOnce;
} else {
mode = ChangeDetectionStrategy.CheckAlways;
}
return mode;
}

View File

@ -1,34 +0,0 @@
import {Injectable} from '@angular/core';
import * as o from '../output/output_ast';
import {CompileElement} from './compile_element';
import {CompileView} from './compile_view';
import {buildView, finishView, ViewCompileDependency} from './view_builder';
import {bindView} from './view_binder';
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
import {TemplateAst} from '../template_ast';
import {CompilerConfig} from '../config';
export class ViewCompileResult {
constructor(public statements: o.Statement[], public viewFactoryVar: string,
public dependencies: ViewCompileDependency[]) {}
}
@Injectable()
export class ViewCompiler {
constructor(private _genConfig: CompilerConfig) {}
compileComponent(component: CompileDirectiveMetadata, template: TemplateAst[],
styles: o.Expression, pipes: CompilePipeMetadata[]): ViewCompileResult {
var statements = [];
var dependencies = [];
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
CompileElement.createNull(), []);
buildView(view, template, dependencies);
// Need to separate binding from creation to be able to refer to
// variables that have been declared after usage.
bindView(view, template);
finishView(view, statements);
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);
}
}

View File

@ -1,173 +0,0 @@
import {
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xit,
} from '@angular/core/testing/testing_internal';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata,
CompileProviderMetadata,
CompileDiDependencyMetadata,
CompileQueryMetadata,
CompileIdentifierMetadata,
CompileFactoryMetadata,
CompileTokenMetadata
} from '@angular/compiler/src/compile_metadata';
import {ViewEncapsulation} from '@angular/core/src/metadata/view';
import {ChangeDetectionStrategy} from '@angular/core/src/change_detection';
import {LifecycleHooks} from '@angular/core/src/metadata/lifecycle_hooks';
export function main() {
describe('CompileMetadata', () => {
var fullTypeMeta: CompileTypeMetadata;
var fullTemplateMeta: CompileTemplateMetadata;
var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => {
var diDep = new CompileDiDependencyMetadata({
isAttribute: true,
isSelf: true,
isHost: true,
isSkipSelf: true,
isOptional: true,
token: new CompileTokenMetadata({value: 'someToken'}),
query: new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'one'})],
descendants: true,
first: true,
propertyName: 'one'
}),
viewQuery: new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'one'})],
descendants: true,
first: true,
propertyName: 'one'
})
});
fullTypeMeta = new CompileTypeMetadata(
{name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: [diDep]});
fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>',
templateUrl: 'someTemplateUrl',
styles: ['someStyle'],
styleUrls: ['someStyleUrl'],
ngContentSelectors: ['*']
});
fullDirectiveMeta = CompileDirectiveMetadata.create({
selector: 'someSelector',
isComponent: true,
type: fullTypeMeta,
template: fullTemplateMeta,
changeDetection: ChangeDetectionStrategy.Default,
inputs: ['someProp'],
outputs: ['someEvent'],
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
lifecycleHooks: [LifecycleHooks.OnChanges],
providers: [
new CompileProviderMetadata({
token: new CompileTokenMetadata({value: 'token'}),
multi: true,
useClass: fullTypeMeta,
useExisting: new CompileTokenMetadata({
identifier: new CompileIdentifierMetadata({name: 'someName'}),
identifierIsInstance: true
}),
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
useValue: 'someValue',
})
],
viewProviders: [
new CompileProviderMetadata({
token: new CompileTokenMetadata({value: 'token'}),
useClass: fullTypeMeta,
useExisting: new CompileTokenMetadata(
{identifier: new CompileIdentifierMetadata({name: 'someName'})}),
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
useValue: 'someValue'
})
],
queries: [
new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'selector'})],
descendants: true,
first: false,
propertyName: 'prop',
read: new CompileTokenMetadata({value: 'readToken'})
})
],
viewQueries: [
new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'selector'})],
descendants: true,
first: false,
propertyName: 'prop',
read: new CompileTokenMetadata({value: 'readToken'})
})
]
});
});
describe('CompileIdentifierMetadata', () => {
it('should serialize with full data', () => {
let full = new CompileIdentifierMetadata(
{name: 'name', moduleUrl: 'module', value: ['one', ['two']]});
expect(CompileIdentifierMetadata.fromJson(full.toJson())).toEqual(full);
});
it('should serialize with no data', () => {
let empty = new CompileIdentifierMetadata();
expect(CompileIdentifierMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
describe('DirectiveMetadata', () => {
it('should serialize with full data', () => {
expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
.toEqual(fullDirectiveMeta);
});
it('should serialize with no data', () => {
var empty = CompileDirectiveMetadata.create();
expect(CompileDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
describe('TypeMetadata', () => {
it('should serialize with full data', () => {
expect(CompileTypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta);
});
it('should serialize with no data', () => {
var empty = new CompileTypeMetadata();
expect(CompileTypeMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
describe('TemplateMetadata', () => {
it('should use ViewEncapsulation.Emulated by default', () => {
expect(new CompileTemplateMetadata({encapsulation: null}).encapsulation)
.toBe(ViewEncapsulation.Emulated);
});
it('should serialize with full data', () => {
expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
.toEqual(fullTemplateMeta);
});
it('should serialize with no data', () => {
var empty = new CompileTemplateMetadata();
expect(CompileTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
});
}

View File

@ -1,393 +0,0 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach
} from '@angular/core/testing/testing_internal';
import {isPresent} from '../../src/facade/lang';
import {
CssToken,
CssScannerError,
CssLexer,
CssLexerMode,
CssTokenType
} from '@angular/compiler/src/css/lexer';
export function main() {
function tokenize(code, trackComments: boolean = false,
mode: CssLexerMode = CssLexerMode.ALL): CssToken[] {
var scanner = new CssLexer().scan(code, trackComments);
scanner.setMode(mode);
var tokens = [];
var output = scanner.scan();
while (output != null) {
var error = output.error;
if (isPresent(error)) {
throw new CssScannerError(error.token, error.rawMessage);
}
tokens.push(output.token);
output = scanner.scan();
}
return tokens;
}
describe('CssLexer', () => {
it('should lex newline characters as whitespace when whitespace mode is on', () => {
var newlines = ["\n", "\r\n", "\r", "\f"];
newlines.forEach((line) => {
var token = tokenize(line, false, CssLexerMode.ALL_TRACK_WS)[0];
expect(token.type).toEqual(CssTokenType.Whitespace);
});
});
it('should combined newline characters as one newline token when whitespace mode is on', () => {
var newlines = ["\n", "\r\n", "\r", "\f"].join("");
var tokens = tokenize(newlines, false, CssLexerMode.ALL_TRACK_WS);
expect(tokens.length).toEqual(1);
expect(tokens[0].type).toEqual(CssTokenType.Whitespace);
});
it('should not consider whitespace or newline values at all when whitespace mode is off',
() => {
var newlines = ["\n", "\r\n", "\r", "\f"].join("");
var tokens = tokenize(newlines);
expect(tokens.length).toEqual(0);
});
it('should lex simple selectors and their inner properties', () => {
var cssCode = "\n" + " .selector { my-prop: my-value; }\n";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.Character);
expect(tokens[0].strValue).toEqual('.');
expect(tokens[1].type).toEqual(CssTokenType.Identifier);
expect(tokens[1].strValue).toEqual('selector');
expect(tokens[2].type).toEqual(CssTokenType.Character);
expect(tokens[2].strValue).toEqual('{');
expect(tokens[3].type).toEqual(CssTokenType.Identifier);
expect(tokens[3].strValue).toEqual('my-prop');
expect(tokens[4].type).toEqual(CssTokenType.Character);
expect(tokens[4].strValue).toEqual(':');
expect(tokens[5].type).toEqual(CssTokenType.Identifier);
expect(tokens[5].strValue).toEqual('my-value');
expect(tokens[6].type).toEqual(CssTokenType.Character);
expect(tokens[6].strValue).toEqual(';');
expect(tokens[7].type).toEqual(CssTokenType.Character);
expect(tokens[7].strValue).toEqual('}');
});
it('should capture the column and line values for each token', () => {
var cssCode = "#id {\n" + " prop:value;\n" + "}";
var tokens = tokenize(cssCode);
// #
expect(tokens[0].type).toEqual(CssTokenType.Character);
expect(tokens[0].column).toEqual(0);
expect(tokens[0].line).toEqual(0);
// id
expect(tokens[1].type).toEqual(CssTokenType.Identifier);
expect(tokens[1].column).toEqual(1);
expect(tokens[1].line).toEqual(0);
// {
expect(tokens[2].type).toEqual(CssTokenType.Character);
expect(tokens[2].column).toEqual(4);
expect(tokens[2].line).toEqual(0);
// prop
expect(tokens[3].type).toEqual(CssTokenType.Identifier);
expect(tokens[3].column).toEqual(2);
expect(tokens[3].line).toEqual(1);
// :
expect(tokens[4].type).toEqual(CssTokenType.Character);
expect(tokens[4].column).toEqual(6);
expect(tokens[4].line).toEqual(1);
// value
expect(tokens[5].type).toEqual(CssTokenType.Identifier);
expect(tokens[5].column).toEqual(7);
expect(tokens[5].line).toEqual(1);
// ;
expect(tokens[6].type).toEqual(CssTokenType.Character);
expect(tokens[6].column).toEqual(12);
expect(tokens[6].line).toEqual(1);
// }
expect(tokens[7].type).toEqual(CssTokenType.Character);
expect(tokens[7].column).toEqual(0);
expect(tokens[7].line).toEqual(2);
});
it('should lex quoted strings and escape accordingly', () => {
var cssCode = "prop: 'some { value } \\' that is quoted'";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.Identifier);
expect(tokens[1].type).toEqual(CssTokenType.Character);
expect(tokens[2].type).toEqual(CssTokenType.String);
expect(tokens[2].strValue).toEqual("'some { value } \\' that is quoted'");
});
it('should treat attribute operators as regular characters', () => {
tokenize('^|~+*').forEach((token) => { expect(token.type).toEqual(CssTokenType.Character); });
});
it('should lex numbers properly and set them as numbers', () => {
var cssCode = "0 1 -2 3.0 -4.001";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.Number);
expect(tokens[0].strValue).toEqual("0");
expect(tokens[1].type).toEqual(CssTokenType.Number);
expect(tokens[1].strValue).toEqual("1");
expect(tokens[2].type).toEqual(CssTokenType.Number);
expect(tokens[2].strValue).toEqual("-2");
expect(tokens[3].type).toEqual(CssTokenType.Number);
expect(tokens[3].strValue).toEqual("3.0");
expect(tokens[4].type).toEqual(CssTokenType.Number);
expect(tokens[4].strValue).toEqual("-4.001");
});
it('should lex @keywords', () => {
var cssCode = "@import()@something";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.AtKeyword);
expect(tokens[0].strValue).toEqual('@import');
expect(tokens[1].type).toEqual(CssTokenType.Character);
expect(tokens[1].strValue).toEqual('(');
expect(tokens[2].type).toEqual(CssTokenType.Character);
expect(tokens[2].strValue).toEqual(')');
expect(tokens[3].type).toEqual(CssTokenType.AtKeyword);
expect(tokens[3].strValue).toEqual('@something');
});
it('should still lex a number even if it has a dimension suffix', () => {
var cssCode = "40% is 40 percent";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.Number);
expect(tokens[0].strValue).toEqual('40');
expect(tokens[1].type).toEqual(CssTokenType.Character);
expect(tokens[1].strValue).toEqual('%');
expect(tokens[2].type).toEqual(CssTokenType.Identifier);
expect(tokens[2].strValue).toEqual('is');
expect(tokens[3].type).toEqual(CssTokenType.Number);
expect(tokens[3].strValue).toEqual('40');
});
it('should allow escaped character and unicode character-strings in CSS selectors', () => {
var cssCode = "\\123456 .some\\thing \{\}";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.Identifier);
expect(tokens[0].strValue).toEqual('\\123456');
expect(tokens[1].type).toEqual(CssTokenType.Character);
expect(tokens[2].type).toEqual(CssTokenType.Identifier);
expect(tokens[2].strValue).toEqual('some\\thing');
});
it('should distinguish identifiers and numbers from special characters', () => {
var cssCode = "one*two=-4+three-4-equals_value$";
var tokens = tokenize(cssCode);
expect(tokens[0].type).toEqual(CssTokenType.Identifier);
expect(tokens[0].strValue).toEqual("one");
expect(tokens[1].type).toEqual(CssTokenType.Character);
expect(tokens[1].strValue).toEqual("*");
expect(tokens[2].type).toEqual(CssTokenType.Identifier);
expect(tokens[2].strValue).toEqual("two");
expect(tokens[3].type).toEqual(CssTokenType.Character);
expect(tokens[3].strValue).toEqual("=");
expect(tokens[4].type).toEqual(CssTokenType.Number);
expect(tokens[4].strValue).toEqual("-4");
expect(tokens[5].type).toEqual(CssTokenType.Character);
expect(tokens[5].strValue).toEqual("+");
expect(tokens[6].type).toEqual(CssTokenType.Identifier);
expect(tokens[6].strValue).toEqual("three-4-equals_value");
expect(tokens[7].type).toEqual(CssTokenType.Character);
expect(tokens[7].strValue).toEqual("$");
});
it('should filter out comments and whitespace by default', () => {
var cssCode = ".selector /* comment */ { /* value */ }";
var tokens = tokenize(cssCode);
expect(tokens[0].strValue).toEqual(".");
expect(tokens[1].strValue).toEqual("selector");
expect(tokens[2].strValue).toEqual("{");
expect(tokens[3].strValue).toEqual("}");
});
it('should track comments when the flag is set to true', () => {
var cssCode = ".selector /* comment */ { /* value */ }";
var trackComments = true;
var tokens = tokenize(cssCode, trackComments, CssLexerMode.ALL_TRACK_WS);
expect(tokens[0].strValue).toEqual(".");
expect(tokens[1].strValue).toEqual("selector");
expect(tokens[2].strValue).toEqual(" ");
expect(tokens[3].type).toEqual(CssTokenType.Comment);
expect(tokens[3].strValue).toEqual("/* comment */");
expect(tokens[4].strValue).toEqual(" ");
expect(tokens[5].strValue).toEqual("{");
expect(tokens[6].strValue).toEqual(" ");
expect(tokens[7].type).toEqual(CssTokenType.Comment);
expect(tokens[7].strValue).toEqual("/* value */");
});
describe('Selector Mode', () => {
it('should throw an error if a selector is being parsed while in the wrong mode', () => {
var cssCode = ".class > tag";
var capturedMessage;
try {
tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK);
} catch (e) {
capturedMessage = e.rawMessage;
}
expect(capturedMessage)
.toMatchPattern(/Unexpected character \[\>\] at column 0:7 in expression/g);
capturedMessage = null;
try {
tokenize(cssCode, false, CssLexerMode.SELECTOR);
} catch (e) {
capturedMessage = e.rawMessage;
}
expect(capturedMessage).toEqual(null);
});
});
describe('Attribute Mode', () => {
it('should consider attribute selectors as valid input and throw when an invalid modifier is used',
() => {
function tokenizeAttr(modifier) {
var cssCode = "value" + modifier + "='something'";
return tokenize(cssCode, false, CssLexerMode.ATTRIBUTE_SELECTOR);
}
expect(tokenizeAttr("*").length).toEqual(4);
expect(tokenizeAttr("|").length).toEqual(4);
expect(tokenizeAttr("^").length).toEqual(4);
expect(tokenizeAttr("$").length).toEqual(4);
expect(tokenizeAttr("~").length).toEqual(4);
expect(tokenizeAttr("").length).toEqual(3);
expect(() => { tokenizeAttr("+"); }).toThrow();
});
});
describe('Media Query Mode', () => {
it('should validate media queries with a reduced subset of valid characters', () => {
function tokenizeQuery(code) { return tokenize(code, false, CssLexerMode.MEDIA_QUERY); }
// the reason why the numbers are so high is because MediaQueries keep
// track of the whitespace values
expect(tokenizeQuery("(prop: value)").length).toEqual(5);
expect(tokenizeQuery("(prop: value) and (prop2: value2)").length).toEqual(11);
expect(tokenizeQuery("tv and (prop: value)").length).toEqual(7);
expect(tokenizeQuery("print and ((prop: value) or (prop2: value2))").length).toEqual(15);
expect(tokenizeQuery("(content: 'something $ crazy inside &')").length).toEqual(5);
expect(() => { tokenizeQuery("(max-height: 10 + 20)"); }).toThrow();
expect(() => { tokenizeQuery("(max-height: fifty < 100)"); }).toThrow();
});
});
describe('Pseudo Selector Mode', () => {
it('should validate pseudo selector identifiers with a reduced subset of valid characters',
() => {
function tokenizePseudo(code) {
return tokenize(code, false, CssLexerMode.PSEUDO_SELECTOR);
}
expect(tokenizePseudo("lang(en-us)").length).toEqual(4);
expect(tokenizePseudo("hover").length).toEqual(1);
expect(tokenizePseudo("focus").length).toEqual(1);
expect(() => { tokenizePseudo("lang(something:broken)"); }).toThrow();
expect(() => { tokenizePseudo("not(.selector)"); }).toThrow();
});
});
describe('Pseudo Selector Mode', () => {
it('should validate pseudo selector identifiers with a reduced subset of valid characters',
() => {
function tokenizePseudo(code) {
return tokenize(code, false, CssLexerMode.PSEUDO_SELECTOR);
}
expect(tokenizePseudo("lang(en-us)").length).toEqual(4);
expect(tokenizePseudo("hover").length).toEqual(1);
expect(tokenizePseudo("focus").length).toEqual(1);
expect(() => { tokenizePseudo("lang(something:broken)"); }).toThrow();
expect(() => { tokenizePseudo("not(.selector)"); }).toThrow();
});
});
describe('Style Block Mode', () => {
it('should style blocks with a reduced subset of valid characters', () => {
function tokenizeStyles(code) { return tokenize(code, false, CssLexerMode.STYLE_BLOCK); }
expect(tokenizeStyles(`
key: value;
prop: 100;
style: value3!important;
`).length)
.toEqual(14);
expect(() => tokenizeStyles(` key$: value; `)).toThrow();
expect(() => tokenizeStyles(` key: value$; `)).toThrow();
expect(() => tokenizeStyles(` key: value + 10; `)).toThrow();
expect(() => tokenizeStyles(` key: &value; `)).toThrow();
});
});
});
}

View File

@ -1,640 +0,0 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach
} from '@angular/core/testing/testing_internal';
import {BaseException} from '../../src/facade/exceptions';
import {
ParsedCssResult,
CssParser,
BlockType,
CssSelectorRuleAST,
CssKeyframeRuleAST,
CssKeyframeDefinitionAST,
CssBlockDefinitionRuleAST,
CssMediaQueryRuleAST,
CssBlockRuleAST,
CssInlineRuleAST,
CssStyleValueAST,
CssSelectorAST,
CssDefinitionAST,
CssStyleSheetAST,
CssRuleAST,
CssBlockAST,
CssParseError
} from '@angular/compiler/src/css/parser';
import {CssLexer} from '@angular/compiler/src/css/lexer';
export function assertTokens(tokens, valuesArr) {
for (var i = 0; i < tokens.length; i++) {
expect(tokens[i].strValue == valuesArr[i]);
}
}
export function main() {
describe('CssParser', () => {
function parse(css): ParsedCssResult {
var lexer = new CssLexer();
var scanner = lexer.scan(css);
var parser = new CssParser(scanner, 'some-fake-file-name.css');
return parser.parse();
}
function makeAST(css): CssStyleSheetAST {
var output = parse(css);
var errors = output.errors;
if (errors.length > 0) {
throw new BaseException(errors.map((error: CssParseError) => error.msg).join(', '));
}
return output.ast;
}
it('should parse CSS into a stylesheet AST', () => {
var styles = `
.selector {
prop: value123;
}
`;
var ast = makeAST(styles);
expect(ast.rules.length).toEqual(1);
var rule = <CssSelectorRuleAST>ast.rules[0];
var selector = rule.selectors[0];
expect(selector.strValue).toEqual('.selector');
var block: CssBlockAST = rule.block;
expect(block.entries.length).toEqual(1);
var definition = <CssDefinitionAST>block.entries[0];
expect(definition.property.strValue).toEqual('prop');
var value = <CssStyleValueAST>definition.value;
expect(value.tokens[0].strValue).toEqual('value123');
});
it('should parse mutliple CSS selectors sharing the same set of styles', () => {
var styles = `
.class, #id, tag, [attr], key + value, * value, :-moz-any-link {
prop: value123;
}
`;
var ast = makeAST(styles);
expect(ast.rules.length).toEqual(1);
var rule = <CssSelectorRuleAST>ast.rules[0];
expect(rule.selectors.length).toBe(7);
assertTokens(rule.selectors[0].tokens, [".", "class"]);
assertTokens(rule.selectors[1].tokens, ["#", "id"]);
assertTokens(rule.selectors[2].tokens, ["tag"]);
assertTokens(rule.selectors[3].tokens, ["[", "attr", "]"]);
assertTokens(rule.selectors[4].tokens, ["key", " ", "+", " ", "value"]);
assertTokens(rule.selectors[5].tokens, ["*", " ", "value"]);
assertTokens(rule.selectors[6].tokens, [":", "-moz-any-link"]);
var style1 = <CssDefinitionAST>rule.block.entries[0];
expect(style1.property.strValue).toEqual("prop");
assertTokens(style1.value.tokens, ["value123"]);
});
it('should parse keyframe rules', () => {
var styles = `
@keyframes rotateMe {
from {
transform: rotate(-360deg);
}
50% {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
`;
var ast = makeAST(styles);
expect(ast.rules.length).toEqual(1);
var rule = <CssKeyframeRuleAST>ast.rules[0];
expect(rule.name.strValue).toEqual('rotateMe');
var block = <CssBlockAST>rule.block;
var fromRule = <CssKeyframeDefinitionAST>block.entries[0];
expect(fromRule.name.strValue).toEqual('from');
var fromStyle = <CssDefinitionAST>(<CssBlockAST>fromRule.block).entries[0];
expect(fromStyle.property.strValue).toEqual('transform');
assertTokens(fromStyle.value.tokens, ['rotate', '(', '-360', 'deg', ')']);
var midRule = <CssKeyframeDefinitionAST>block.entries[1];
expect(midRule.name.strValue).toEqual('50%');
var midStyle = <CssDefinitionAST>(<CssBlockAST>midRule.block).entries[0];
expect(midStyle.property.strValue).toEqual('transform');
assertTokens(midStyle.value.tokens, ['rotate', '(', '0', 'deg', ')']);
var toRule = <CssKeyframeDefinitionAST>block.entries[2];
expect(toRule.name.strValue).toEqual('to');
var toStyle = <CssDefinitionAST>(<CssBlockAST>toRule.block).entries[0];
expect(toStyle.property.strValue).toEqual('transform');
assertTokens(toStyle.value.tokens, ['rotate', '(', '360', 'deg', ')']);
});
it('should parse media queries into a stylesheet AST', () => {
var styles = `
@media all and (max-width:100px) {
.selector {
prop: value123;
}
}
`;
var ast = makeAST(styles);
expect(ast.rules.length).toEqual(1);
var rule = <CssMediaQueryRuleAST>ast.rules[0];
assertTokens(rule.query, ['all', 'and', '(', 'max-width', ':', '100', 'px', ')']);
var block = <CssBlockAST>rule.block;
expect(block.entries.length).toEqual(1);
var rule2 = <CssSelectorRuleAST>block.entries[0];
expect(rule2.selectors[0].strValue).toEqual('.selector');
var block2 = <CssBlockAST>rule2.block;
expect(block2.entries.length).toEqual(1);
});
it('should parse inline CSS values', () => {
var styles = `
@import url('remote.css');
@charset "UTF-8";
@namespace ng url(http://angular.io/namespace/ng);
`;
var ast = makeAST(styles);
var importRule = <CssInlineRuleAST>ast.rules[0];
expect(importRule.type).toEqual(BlockType.Import);
assertTokens(importRule.value.tokens, ["url", "(", "remote", ".", "css", ")"]);
var charsetRule = <CssInlineRuleAST>ast.rules[1];
expect(charsetRule.type).toEqual(BlockType.Charset);
assertTokens(charsetRule.value.tokens, ["UTF-8"]);
var namespaceRule = <CssInlineRuleAST>ast.rules[2];
expect(namespaceRule.type).toEqual(BlockType.Namespace);
assertTokens(namespaceRule.value.tokens,
["ng", "url", "(", "http://angular.io/namespace/ng", ")"]);
});
it('should parse CSS values that contain functions and leave the inner function data untokenized',
() => {
var styles = `
.class {
background: url(matias.css);
animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
height: calc(100% - 50px);
}
`;
var ast = makeAST(styles);
expect(ast.rules.length).toEqual(1);
var defs = (<CssSelectorRuleAST>ast.rules[0]).block.entries;
expect(defs.length).toEqual(3);
assertTokens((<CssDefinitionAST>defs[0]).value.tokens, ['url', '(', 'matias.css', ')']);
assertTokens((<CssDefinitionAST>defs[1]).value.tokens,
['cubic-bezier', '(', '0.755, 0.050, 0.855, 0.060', ')']);
assertTokens((<CssDefinitionAST>defs[2]).value.tokens, ['calc', '(', '100% - 50px', ')']);
});
it('should parse un-named block-level CSS values', () => {
var styles = `
@font-face {
font-family: "Matias";
font-weight: bold;
src: url(font-face.ttf);
}
@viewport {
max-width: 100px;
min-height: 1000px;
}
`;
var ast = makeAST(styles);
var fontFaceRule = <CssBlockRuleAST>ast.rules[0];
expect(fontFaceRule.type).toEqual(BlockType.FontFace);
expect(fontFaceRule.block.entries.length).toEqual(3);
var viewportRule = <CssBlockRuleAST>ast.rules[1];
expect(viewportRule.type).toEqual(BlockType.Viewport);
expect(viewportRule.block.entries.length).toEqual(2);
});
it('should parse multiple levels of semicolons', () => {
var styles = `
;;;
@import url('something something')
;;;;;;;;
;;;;;;;;
;@font-face {
;src : url(font-face.ttf);;;;;;;;
;;;-webkit-animation:my-animation
};;;
@media all and (max-width:100px)
{;
.selector {prop: value123;};
;.selector2{prop:1}}
`;
var ast = makeAST(styles);
var importRule = <CssInlineRuleAST>ast.rules[0];
expect(importRule.type).toEqual(BlockType.Import);
assertTokens(importRule.value.tokens, ["url", "(", "something something", ")"]);
var fontFaceRule = <CssBlockRuleAST>ast.rules[1];
expect(fontFaceRule.type).toEqual(BlockType.FontFace);
expect(fontFaceRule.block.entries.length).toEqual(2);
var mediaQueryRule = <CssMediaQueryRuleAST>ast.rules[2];
assertTokens(mediaQueryRule.query, ['all', 'and', '(', 'max-width', ':', '100', 'px', ')']);
expect(mediaQueryRule.block.entries.length).toEqual(2);
});
it('should throw an error if an unknown @value block rule is parsed', () => {
var styles = `
@matias { hello: there; }
`;
expect(() => {
makeAST(styles);
}).toThrowError(/^CSS Parse Error: The CSS "at" rule "@matias" is not allowed to used here/g);
});
it('should parse empty rules', () => {
var styles = `
.empty-rule { }
.somewhat-empty-rule { /* property: value; */ }
.non-empty-rule { property: value; }
`;
var ast = makeAST(styles);
var rules = ast.rules;
expect((<CssSelectorRuleAST>rules[0]).block.entries.length).toEqual(0);
expect((<CssSelectorRuleAST>rules[1]).block.entries.length).toEqual(0);
expect((<CssSelectorRuleAST>rules[2]).block.entries.length).toEqual(1);
});
it('should parse the @document rule', () => {
var styles = `
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
/* CSS rules here apply to:
- The page "http://www.w3.org/".
- Any page whose URL begins with "http://www.w3.org/Style/"
- Any page whose URL's host is "mozilla.org" or ends with
".mozilla.org"
- Any page whose URL starts with "https:" */
/* make the above-mentioned pages really ugly */
body {
color: purple;
background: yellow;
}
}
`;
var ast = makeAST(styles);
var rules = ast.rules;
var documentRule = <CssBlockDefinitionRuleAST>rules[0];
expect(documentRule.type).toEqual(BlockType.Document);
var rule = <CssSelectorRuleAST>documentRule.block.entries[0];
expect(rule.strValue).toEqual("body");
});
it('should parse the @page rule', () => {
var styles = `
@page one {
.selector { prop: value; }
}
@page two {
.selector2 { prop: value2; }
}
`;
var ast = makeAST(styles);
var rules = ast.rules;
var pageRule1 = <CssBlockDefinitionRuleAST>rules[0];
expect(pageRule1.strValue).toEqual("one");
expect(pageRule1.type).toEqual(BlockType.Page);
var pageRule2 = <CssBlockDefinitionRuleAST>rules[1];
expect(pageRule2.strValue).toEqual("two");
expect(pageRule2.type).toEqual(BlockType.Page);
var selectorOne = <CssSelectorRuleAST>pageRule1.block.entries[0];
expect(selectorOne.strValue).toEqual('.selector');
var selectorTwo = <CssSelectorRuleAST>pageRule2.block.entries[0];
expect(selectorTwo.strValue).toEqual('.selector2');
});
it('should parse the @supports rule', () => {
var styles = `
@supports (animation-name: "rotate") {
a:hover { animation: rotate 1s; }
}
`;
var ast = makeAST(styles);
var rules = ast.rules;
var supportsRule = <CssBlockDefinitionRuleAST>rules[0];
assertTokens(supportsRule.query, ['(', 'animation-name', ':', 'rotate', ')']);
expect(supportsRule.type).toEqual(BlockType.Supports);
var selectorOne = <CssSelectorRuleAST>supportsRule.block.entries[0];
expect(selectorOne.strValue).toEqual('a:hover');
});
it('should collect multiple errors during parsing', () => {
var styles = `
.class$value { something: something }
@custom { something: something }
#id { cool^: value }
`;
var output = parse(styles);
expect(output.errors.length).toEqual(3);
});
it('should recover from selector errors and continue parsing', () => {
var styles = `
tag& { key: value; }
.%tag { key: value; }
#tag$ { key: value; }
`;
var output = parse(styles);
var errors = output.errors;
var ast = output.ast;
expect(errors.length).toEqual(3);
expect(ast.rules.length).toEqual(3);
var rule1 = <CssSelectorRuleAST>ast.rules[0];
expect(rule1.selectors[0].strValue).toEqual("tag&");
expect(rule1.block.entries.length).toEqual(1);
var rule2 = <CssSelectorRuleAST>ast.rules[1];
expect(rule2.selectors[0].strValue).toEqual(".%tag");
expect(rule2.block.entries.length).toEqual(1);
var rule3 = <CssSelectorRuleAST>ast.rules[2];
expect(rule3.selectors[0].strValue).toEqual("#tag$");
expect(rule3.block.entries.length).toEqual(1);
});
it('should throw an error when parsing invalid CSS Selectors', () => {
var styles = '.class[[prop%=value}] { style: val; }';
var output = parse(styles);
var errors = output.errors;
expect(errors.length).toEqual(3);
expect(errors[0].msg).toMatchPattern(/Unexpected character \[\[\] at column 0:7/g);
expect(errors[1].msg).toMatchPattern(/Unexpected character \[%\] at column 0:12/g);
expect(errors[2].msg).toMatchPattern(/Unexpected character \[}\] at column 0:19/g);
});
it('should throw an error if an attribute selector is not closed properly', () => {
var styles = '.class[prop=value { style: val; }';
var output = parse(styles);
var errors = output.errors;
expect(errors[0].msg).toMatchPattern(/Unbalanced CSS attribute selector at column 0:12/g);
});
it('should throw an error if a pseudo function selector is not closed properly', () => {
var styles = 'body:lang(en { key:value; }';
var output = parse(styles);
var errors = output.errors;
expect(errors[0].msg)
.toMatchPattern(/Unbalanced pseudo selector function value at column 0:10/g);
});
it('should raise an error when a semi colon is missing from a CSS style/pair that isn\'t the last entry',
() => {
var styles = `.class {
color: red
background: blue
}`;
var output = parse(styles);
var errors = output.errors;
expect(errors.length).toEqual(1);
expect(errors[0].msg)
.toMatchPattern(
/The CSS key\/value definition did not end with a semicolon at column 1:15/g);
});
it('should parse the inner value of a :not() pseudo-selector as a CSS selector', () => {
var styles = `div:not(.ignore-this-div) {
prop: value;
}`;
var output = parse(styles);
var errors = output.errors;
var ast = output.ast;
expect(errors.length).toEqual(0);
var rule1 = <CssSelectorRuleAST>ast.rules[0];
expect(rule1.selectors.length).toEqual(1);
var selector = rule1.selectors[0];
assertTokens(selector.tokens, ['div', ':', 'not', '(', '.', 'ignore-this-div', ')']);
});
it('should raise parse errors when CSS key/value pairs are invalid', () => {
var styles = `.class {
background color: value;
color: value
font-size;
font-weight
}`;
var output = parse(styles);
var errors = output.errors;
expect(errors.length).toEqual(4);
expect(errors[0].msg)
.toMatchPattern(
/Identifier does not match expected Character value \("color" should match ":"\) at column 1:19/g);
expect(errors[1].msg)
.toMatchPattern(
/The CSS key\/value definition did not end with a semicolon at column 2:15/g);
expect(errors[2].msg)
.toMatchPattern(/The CSS property was not paired with a style value at column 3:8/g);
expect(errors[3].msg)
.toMatchPattern(/The CSS property was not paired with a style value at column 4:8/g);
});
it('should recover from CSS key/value parse errors', () => {
var styles = `
.problem-class { background color: red; color: white; }
.good-boy-class { background-color: red; color: white; }
`;
var output = parse(styles);
var ast = output.ast;
expect(ast.rules.length).toEqual(2);
var rule1 = <CssSelectorRuleAST>ast.rules[0];
expect(rule1.block.entries.length).toEqual(2);
var style1 = <CssDefinitionAST>rule1.block.entries[0];
expect(style1.property.strValue).toEqual('background color');
assertTokens(style1.value.tokens, ['red']);
var style2 = <CssDefinitionAST>rule1.block.entries[1];
expect(style2.property.strValue).toEqual('color');
assertTokens(style2.value.tokens, ['white']);
});
it('should parse minified CSS content properly', () => {
// this code was taken from the angular.io webpage's CSS code
var styles = `
.is-hidden{display:none!important}
.is-visible{display:block!important}
.is-visually-hidden{height:1px;width:1px;overflow:hidden;opacity:0.01;position:absolute;bottom:0;right:0;z-index:1}
.grid-fluid,.grid-fixed{margin:0 auto}
.grid-fluid .c1,.grid-fixed .c1,.grid-fluid .c2,.grid-fixed .c2,.grid-fluid .c3,.grid-fixed .c3,.grid-fluid .c4,.grid-fixed .c4,.grid-fluid .c5,.grid-fixed .c5,.grid-fluid .c6,.grid-fixed .c6,.grid-fluid .c7,.grid-fixed .c7,.grid-fluid .c8,.grid-fixed .c8,.grid-fluid .c9,.grid-fixed .c9,.grid-fluid .c10,.grid-fixed .c10,.grid-fluid .c11,.grid-fixed .c11,.grid-fluid .c12,.grid-fixed .c12{display:inline;float:left}
.grid-fluid .c1.grid-right,.grid-fixed .c1.grid-right,.grid-fluid .c2.grid-right,.grid-fixed .c2.grid-right,.grid-fluid .c3.grid-right,.grid-fixed .c3.grid-right,.grid-fluid .c4.grid-right,.grid-fixed .c4.grid-right,.grid-fluid .c5.grid-right,.grid-fixed .c5.grid-right,.grid-fluid .c6.grid-right,.grid-fixed .c6.grid-right,.grid-fluid .c7.grid-right,.grid-fixed .c7.grid-right,.grid-fluid .c8.grid-right,.grid-fixed .c8.grid-right,.grid-fluid .c9.grid-right,.grid-fixed .c9.grid-right,.grid-fluid .c10.grid-right,.grid-fixed .c10.grid-right,.grid-fluid .c11.grid-right,.grid-fixed .c11.grid-right,.grid-fluid .c12.grid-right,.grid-fixed .c12.grid-right{float:right}
.grid-fluid .c1.nb,.grid-fixed .c1.nb,.grid-fluid .c2.nb,.grid-fixed .c2.nb,.grid-fluid .c3.nb,.grid-fixed .c3.nb,.grid-fluid .c4.nb,.grid-fixed .c4.nb,.grid-fluid .c5.nb,.grid-fixed .c5.nb,.grid-fluid .c6.nb,.grid-fixed .c6.nb,.grid-fluid .c7.nb,.grid-fixed .c7.nb,.grid-fluid .c8.nb,.grid-fixed .c8.nb,.grid-fluid .c9.nb,.grid-fixed .c9.nb,.grid-fluid .c10.nb,.grid-fixed .c10.nb,.grid-fluid .c11.nb,.grid-fixed .c11.nb,.grid-fluid .c12.nb,.grid-fixed .c12.nb{margin-left:0}
.grid-fluid .c1.na,.grid-fixed .c1.na,.grid-fluid .c2.na,.grid-fixed .c2.na,.grid-fluid .c3.na,.grid-fixed .c3.na,.grid-fluid .c4.na,.grid-fixed .c4.na,.grid-fluid .c5.na,.grid-fixed .c5.na,.grid-fluid .c6.na,.grid-fixed .c6.na,.grid-fluid .c7.na,.grid-fixed .c7.na,.grid-fluid .c8.na,.grid-fixed .c8.na,.grid-fluid .c9.na,.grid-fixed .c9.na,.grid-fluid .c10.na,.grid-fixed .c10.na,.grid-fluid .c11.na,.grid-fixed .c11.na,.grid-fluid .c12.na,.grid-fixed .c12.na{margin-right:0}
`;
var output = parse(styles);
var errors = output.errors;
expect(errors.length).toEqual(0);
var ast = output.ast;
expect(ast.rules.length).toEqual(8);
});
it('should parse a snippet of keyframe code from animate.css properly', () => {
// this code was taken from the angular.io webpage's CSS code
var styles = `
@charset "UTF-8";
/*!
* animate.css -http://daneden.me/animate
* Version - 3.5.1
* Licensed under the MIT license - http://opensource.org/licenses/MIT
*
* Copyright (c) 2016 Daniel Eden
*/
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
.animated.hinge {
-webkit-animation-duration: 2s;
animation-duration: 2s;
}
.animated.flipOutX,
.animated.flipOutY,
.animated.bounceIn,
.animated.bounceOut {
-webkit-animation-duration: .75s;
animation-duration: .75s;
}
@-webkit-keyframes bounce {
from, 20%, 53%, 80%, to {
-webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%, 43% {
-webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -30px, 0);
transform: translate3d(0, -30px, 0);
}
70% {
-webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -15px, 0);
transform: translate3d(0, -15px, 0);
}
90% {
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
`;
var output = parse(styles);
var errors = output.errors;
expect(errors.length).toEqual(0);
var ast = output.ast;
expect(ast.rules.length).toEqual(6);
var finalRule = <CssBlockRuleAST>ast.rules[ast.rules.length - 1];
expect(finalRule.type).toEqual(BlockType.Keyframes);
expect(finalRule.block.entries.length).toEqual(4);
});
});
}

View File

@ -1,252 +0,0 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach
} from '@angular/core/testing/testing_internal';
import {NumberWrapper, StringWrapper, isPresent} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {
CssToken,
CssParser,
CssParseError,
BlockType,
CssAST,
CssSelectorRuleAST,
CssKeyframeRuleAST,
CssKeyframeDefinitionAST,
CssBlockDefinitionRuleAST,
CssMediaQueryRuleAST,
CssBlockRuleAST,
CssInlineRuleAST,
CssStyleValueAST,
CssSelectorAST,
CssDefinitionAST,
CssStyleSheetAST,
CssRuleAST,
CssBlockAST,
CssASTVisitor,
CssUnknownTokenListAST
} from '@angular/compiler/src/css/parser';
import {CssLexer} from '@angular/compiler/src/css/lexer';
function _assertTokens(tokens, valuesArr) {
for (var i = 0; i < tokens.length; i++) {
expect(tokens[i].strValue == valuesArr[i]);
}
}
class MyVisitor implements CssASTVisitor {
captures: {[key: string]: any[]} = {};
_capture(method, ast, context) {
this.captures[method] = isPresent(this.captures[method]) ? this.captures[method] : [];
this.captures[method].push([ast, context]);
}
constructor(ast: CssStyleSheetAST, context?: any) { ast.visit(this, context); }
visitCssValue(ast, context?: any): void { this._capture("visitCssValue", ast, context); }
visitInlineCssRule(ast, context?: any): void {
this._capture("visitInlineCssRule", ast, context);
}
visitCssKeyframeRule(ast: CssKeyframeRuleAST, context?: any): void {
this._capture("visitCssKeyframeRule", ast, context);
ast.block.visit(this, context);
}
visitCssKeyframeDefinition(ast: CssKeyframeDefinitionAST, context?: any): void {
this._capture("visitCssKeyframeDefinition", ast, context);
ast.block.visit(this, context);
}
visitCssMediaQueryRule(ast: CssMediaQueryRuleAST, context?: any): void {
this._capture("visitCssMediaQueryRule", ast, context);
ast.block.visit(this, context);
}
visitCssSelectorRule(ast: CssSelectorRuleAST, context?: any): void {
this._capture("visitCssSelectorRule", ast, context);
ast.selectors.forEach((selAST: CssSelectorAST) => { selAST.visit(this, context); });
ast.block.visit(this, context);
}
visitCssSelector(ast: CssSelectorAST, context?: any): void {
this._capture("visitCssSelector", ast, context);
}
visitCssDefinition(ast: CssDefinitionAST, context?: any): void {
this._capture("visitCssDefinition", ast, context);
ast.value.visit(this, context);
}
visitCssBlock(ast: CssBlockAST, context?: any): void {
this._capture("visitCssBlock", ast, context);
ast.entries.forEach((entryAST: CssAST) => { entryAST.visit(this, context); });
}
visitCssStyleSheet(ast: CssStyleSheetAST, context?: any): void {
this._capture("visitCssStyleSheet", ast, context);
ast.rules.forEach((ruleAST: CssRuleAST) => { ruleAST.visit(this, context); });
}
visitUnkownRule(ast: CssUnknownTokenListAST, context?: any): void {
// nothing
}
}
export function main() {
function parse(cssCode: string) {
var lexer = new CssLexer();
var scanner = lexer.scan(cssCode);
var parser = new CssParser(scanner, 'some-fake-file-name.css');
var output = parser.parse();
var errors = output.errors;
if (errors.length > 0) {
throw new BaseException(errors.map((error: CssParseError) => error.msg).join(', '));
}
return output.ast;
}
describe('CSS parsing and visiting', () => {
var ast;
var context = {};
beforeEach(() => {
var cssCode = `
.rule1 { prop1: value1 }
.rule2 { prop2: value2 }
@media all (max-width: 100px) {
#id { prop3 :value3; }
}
@import url(file.css);
@keyframes rotate {
from {
prop4: value4;
}
50%, 100% {
prop5: value5;
}
}
`;
ast = parse(cssCode);
});
it('should parse and visit a stylesheet', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitCssStyleSheet'];
expect(captures.length).toEqual(1);
var capture = captures[0];
expect(capture[0]).toEqual(ast);
expect(capture[1]).toEqual(context);
});
it('should parse and visit each of the stylesheet selectors', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitCssSelectorRule'];
expect(captures.length).toEqual(3);
var rule1 = <CssSelectorRuleAST>captures[0][0];
expect(rule1).toEqual(ast.rules[0]);
_assertTokens(rule1.selectors[0].tokens, ['.', 'rule1']);
var rule2 = <CssSelectorRuleAST>captures[1][0];
expect(rule2).toEqual(ast.rules[1]);
_assertTokens(rule2.selectors[0].tokens, ['.', 'rule2']);
var rule3 = captures[2][0];
expect(rule3).toEqual((<CssMediaQueryRuleAST>ast.rules[2]).block.entries[0]);
_assertTokens(rule3.selectors[0].tokens, ['#', 'rule3']);
});
it('should parse and visit each of the stylesheet style key/value definitions', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitCssDefinition'];
expect(captures.length).toEqual(5);
var def1 = <CssDefinitionAST>captures[0][0];
expect(def1.property.strValue).toEqual('prop1');
expect(def1.value.tokens[0].strValue).toEqual('value1');
var def2 = <CssDefinitionAST>captures[1][0];
expect(def2.property.strValue).toEqual('prop2');
expect(def2.value.tokens[0].strValue).toEqual('value2');
var def3 = <CssDefinitionAST>captures[2][0];
expect(def3.property.strValue).toEqual('prop3');
expect(def3.value.tokens[0].strValue).toEqual('value3');
var def4 = <CssDefinitionAST>captures[3][0];
expect(def4.property.strValue).toEqual('prop4');
expect(def4.value.tokens[0].strValue).toEqual('value4');
var def5 = <CssDefinitionAST>captures[4][0];
expect(def5.property.strValue).toEqual('prop5');
expect(def5.value.tokens[0].strValue).toEqual('value5');
});
it('should parse and visit the associated media query values', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitCssMediaQueryRule'];
expect(captures.length).toEqual(1);
var query1 = <CssMediaQueryRuleAST>captures[0][0];
_assertTokens(query1.query, ["all", "and", "(", "max-width", "100", "px", ")"]);
expect(query1.block.entries.length).toEqual(1);
});
it('should parse and visit the associated "@inline" rule values', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitInlineCssRule'];
expect(captures.length).toEqual(1);
var query1 = <CssInlineRuleAST>captures[0][0];
expect(query1.type).toEqual(BlockType.Import);
_assertTokens(query1.value.tokens, ['url', '(', 'file.css', ')']);
});
it('should parse and visit the keyframe blocks', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitCssKeyframeRule'];
expect(captures.length).toEqual(1);
var keyframe1 = <CssKeyframeRuleAST>captures[0][0];
expect(keyframe1.name.strValue).toEqual('rotate');
expect(keyframe1.block.entries.length).toEqual(2);
});
it('should parse and visit the associated keyframe rules', () => {
var visitor = new MyVisitor(ast, context);
var captures = visitor.captures['visitCssKeyframeDefinition'];
expect(captures.length).toEqual(2);
var def1 = <CssKeyframeDefinitionAST>captures[0][0];
_assertTokens(def1.steps, ['from']);
expect(def1.block.entries.length).toEqual(1);
var def2 = <CssKeyframeDefinitionAST>captures[1][0];
_assertTokens(def2.steps, ['50%', '100%']);
expect(def2.block.entries.length).toEqual(1);
});
});
}

View File

@ -1,93 +0,0 @@
import {HtmlParser, HtmlParseTreeResult, HtmlTreeError} from '@angular/compiler/src/html_parser';
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
HtmlExpansionAst,
HtmlExpansionCaseAst,
htmlVisitAll
} from '@angular/compiler/src/html_ast';
import {ParseError, ParseLocation} from '@angular/compiler/src/parse_util';
import {BaseException} from '../src/facade/exceptions';
export function humanizeDom(parseResult: HtmlParseTreeResult): any[] {
if (parseResult.errors.length > 0) {
var errorString = parseResult.errors.join('\n');
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
}
var humanizer = new _Humanizer(false);
htmlVisitAll(humanizer, parseResult.rootNodes);
return humanizer.result;
}
export function humanizeDomSourceSpans(parseResult: HtmlParseTreeResult): any[] {
if (parseResult.errors.length > 0) {
var errorString = parseResult.errors.join('\n');
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
}
var humanizer = new _Humanizer(true);
htmlVisitAll(humanizer, parseResult.rootNodes);
return humanizer.result;
}
export function humanizeLineColumn(location: ParseLocation): string {
return `${location.line}:${location.col}`;
}
class _Humanizer implements HtmlAstVisitor {
result: any[] = [];
elDepth: number = 0;
constructor(private includeSourceSpan: boolean){};
visitElement(ast: HtmlElementAst, context: any): any {
var res = this._appendContext(ast, [HtmlElementAst, ast.name, this.elDepth++]);
this.result.push(res);
htmlVisitAll(this, ast.attrs);
htmlVisitAll(this, ast.children);
this.elDepth--;
return null;
}
visitAttr(ast: HtmlAttrAst, context: any): any {
var res = this._appendContext(ast, [HtmlAttrAst, ast.name, ast.value]);
this.result.push(res);
return null;
}
visitText(ast: HtmlTextAst, context: any): any {
var res = this._appendContext(ast, [HtmlTextAst, ast.value, this.elDepth]);
this.result.push(res);
return null;
}
visitComment(ast: HtmlCommentAst, context: any): any {
var res = this._appendContext(ast, [HtmlCommentAst, ast.value, this.elDepth]);
this.result.push(res);
return null;
}
visitExpansion(ast: HtmlExpansionAst, context: any): any {
var res = this._appendContext(ast, [HtmlExpansionAst, ast.switchValue, ast.type]);
this.result.push(res);
htmlVisitAll(this, ast.cases);
return null;
}
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any {
var res = this._appendContext(ast, [HtmlExpansionCaseAst, ast.value]);
this.result.push(res);
return null;
}
private _appendContext(ast: HtmlAst, input: any[]): any[] {
if (!this.includeSourceSpan) return input;
input.push(ast.sourceSpan.toString());
return input;
}
}

View File

@ -1,340 +0,0 @@
import {
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xdescribe,
xit
} from '@angular/core/testing/testing_internal';
import {I18nHtmlParser} from '@angular/compiler/src/i18n/i18n_html_parser';
import {Message, id} from '@angular/compiler/src/i18n/message';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {StringMapWrapper} from '../../src/facade/collection';
import {HtmlParser, HtmlParseTreeResult} from '@angular/compiler/src/html_parser';
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
htmlVisitAll
} from '@angular/compiler/src/html_ast';
import {serializeXmb, deserializeXmb} from '@angular/compiler/src/i18n/xmb_serializer';
import {ParseError, ParseLocation} from '@angular/compiler/src/parse_util';
import {humanizeDom} from '@angular/compiler/test/html_ast_spec_utils';
export function main() {
describe('I18nHtmlParser', () => {
function parse(template: string, messages: {[key: string]: string}): HtmlParseTreeResult {
var parser = new Parser(new Lexer());
let htmlParser = new HtmlParser();
let msgs = '';
StringMapWrapper.forEach(messages, (v, k) => msgs += `<msg id="${k}">${v}</msg>`);
let res = deserializeXmb(`<message-bundle>${msgs}</message-bundle>`, 'someUrl');
return new I18nHtmlParser(htmlParser, parser, res.content, res.messages)
.parse(template, "someurl", true);
}
it("should delegate to the provided parser when no i18n", () => {
expect(humanizeDom(parse('<div>a</div>', {})))
.toEqual([[HtmlElementAst, 'div', 0], [HtmlTextAst, 'a', 1]]);
});
it("should replace attributes", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message("some message", "meaning", null))] = "another message";
expect(humanizeDom(parse("<div value='some message' i18n-value='meaning|comment'></div>",
translations)))
.toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', 'another message']]);
});
it("should replace elements with the i18n attr", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message("message", "meaning", null))] = "another message";
expect(humanizeDom(parse("<div i18n='meaning|desc'>message</div>", translations)))
.toEqual([[HtmlElementAst, 'div', 0], [HtmlTextAst, 'another message', 1]]);
});
it("should handle interpolation", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="0"/> and <ph name="1"/>', null, null))] =
'<ph name="1"/> or <ph name="0"/>';
expect(humanizeDom(parse("<div value='{{a}} and {{b}}' i18n-value></div>", translations)))
.toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', '{{b}} or {{a}}']]);
});
it('should handle interpolation with custom placeholder names', () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="FIRST"/> and <ph name="SECOND"/>', null, null))] =
'<ph name="SECOND"/> or <ph name="FIRST"/>';
expect(
humanizeDom(parse(
`<div value='{{a //i18n(ph="FIRST")}} and {{b //i18n(ph="SECOND")}}' i18n-value></div>`,
translations)))
.toEqual([
[HtmlElementAst, 'div', 0],
[HtmlAttrAst, 'value', '{{b //i18n(ph="SECOND")}} or {{a //i18n(ph="FIRST")}}']
]);
});
it('should handle interpolation with duplicate placeholder names', () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="FIRST"/> and <ph name="FIRST_1"/>', null, null))] =
'<ph name="FIRST_1"/> or <ph name="FIRST"/>';
expect(
humanizeDom(parse(
`<div value='{{a //i18n(ph="FIRST")}} and {{b //i18n(ph="FIRST")}}' i18n-value></div>`,
translations)))
.toEqual([
[HtmlElementAst, 'div', 0],
[HtmlAttrAst, 'value', '{{b //i18n(ph="FIRST")}} or {{a //i18n(ph="FIRST")}}']
]);
});
it("should handle nested html", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="e0">a</ph><ph name="e2">b</ph>', null, null))] =
'<ph name="e2">B</ph><ph name="e0">A</ph>';
expect(humanizeDom(parse('<div i18n><a>a</a><b>b</b></div>', translations)))
.toEqual([
[HtmlElementAst, 'div', 0],
[HtmlElementAst, 'b', 1],
[HtmlTextAst, 'B', 2],
[HtmlElementAst, 'a', 1],
[HtmlTextAst, 'A', 2],
]);
});
it("should support interpolation", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message(
'<ph name="e0">a</ph><ph name="e2"><ph name="t3">b<ph name="0"/></ph></ph>', null,
null))] = '<ph name="e2"><ph name="t3"><ph name="0"/>B</ph></ph><ph name="e0">A</ph>';
expect(humanizeDom(parse('<div i18n><a>a</a><b>b{{i}}</b></div>', translations)))
.toEqual([
[HtmlElementAst, 'div', 0],
[HtmlElementAst, 'b', 1],
[HtmlTextAst, '{{i}}B', 2],
[HtmlElementAst, 'a', 1],
[HtmlTextAst, 'A', 2],
]);
});
it("should i18n attributes of placeholder elements", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="e0">a</ph>', null, null))] = '<ph name="e0">A</ph>';
translations[id(new Message('b', null, null))] = 'B';
expect(humanizeDom(parse('<div i18n><a value="b" i18n-value>a</a></div>', translations)))
.toEqual([
[HtmlElementAst, 'div', 0],
[HtmlElementAst, 'a', 1],
[HtmlAttrAst, 'value', "B"],
[HtmlTextAst, 'A', 2],
]);
});
it("should preserve non-i18n attributes", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('message', null, null))] = 'another message';
expect(humanizeDom(parse('<div i18n value="b">message</div>', translations)))
.toEqual([
[HtmlElementAst, 'div', 0],
[HtmlAttrAst, 'value', "b"],
[HtmlTextAst, 'another message', 1]
]);
});
it('should extract from partitions', () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('message1', 'meaning1', null))] = 'another message1';
translations[id(new Message('message2', 'meaning2', null))] = 'another message2';
let res = parse(`<!-- i18n: meaning1|desc1 -->message1<!-- /i18n --><!-- i18n: meaning2|desc2 -->message2<!-- /i18n -->`, translations);
expect(humanizeDom(res))
.toEqual([
[HtmlTextAst, 'another message1', 0],
[HtmlTextAst, 'another message2', 0],
]);
});
it("should preserve original positions", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="e0">a</ph><ph name="e2">b</ph>', null, null))] =
'<ph name="e2">B</ph><ph name="e0">A</ph>';
let res =
(<any>parse('<div i18n><a>a</a><b>b</b></div>', translations).rootNodes[0]).children;
expect(res[0].sourceSpan.start.offset).toEqual(18);
expect(res[1].sourceSpan.start.offset).toEqual(10);
});
it("should handle the plural expansion form", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('zero<ph name="e1">bold</ph>', "plural_0", null))] =
'ZERO<ph name="e1">BOLD</ph>';
let res = parse(`{messages.length, plural,=0 {zero<b>bold</b>}}`, translations);
expect(humanizeDom(res))
.toEqual([
[HtmlElementAst, 'ul', 0],
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
[HtmlElementAst, 'template', 1],
[HtmlAttrAst, 'ngPluralCase', '0'],
[HtmlElementAst, 'li', 2],
[HtmlTextAst, 'ZERO', 3],
[HtmlElementAst, 'b', 3],
[HtmlTextAst, 'BOLD', 4]
]);
});
it("should handle nested expansion forms", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('m', "gender_m", null))] = 'M';
let res = parse(`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`, translations);
expect(humanizeDom(res))
.toEqual([
[HtmlElementAst, 'ul', 0],
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
[HtmlElementAst, 'template', 1],
[HtmlAttrAst, 'ngPluralCase', '0'],
[HtmlElementAst, 'li', 2],
[HtmlElementAst, 'ul', 3],
[HtmlAttrAst, '[ngSwitch]', 'p.gender'],
[HtmlElementAst, 'template', 4],
[HtmlAttrAst, 'ngSwitchWhen', 'm'],
[HtmlElementAst, 'li', 5],
[HtmlTextAst, 'M', 6],
[HtmlTextAst, ' ', 3]
]);
});
it("should correctly set source code positions", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('<ph name="e0">bold</ph>', "plural_0", null))] =
'<ph name="e0">BOLD</ph>';
let nodes = parse(`{messages.length, plural,=0 {<b>bold</b>}}`, translations).rootNodes;
let ul: HtmlElementAst = <HtmlElementAst>nodes[0];
expect(ul.sourceSpan.start.col).toEqual(0);
expect(ul.sourceSpan.end.col).toEqual(42);
expect(ul.startSourceSpan.start.col).toEqual(0);
expect(ul.startSourceSpan.end.col).toEqual(42);
expect(ul.endSourceSpan.start.col).toEqual(0);
expect(ul.endSourceSpan.end.col).toEqual(42);
let switchExp = ul.attrs[0];
expect(switchExp.sourceSpan.start.col).toEqual(1);
expect(switchExp.sourceSpan.end.col).toEqual(16);
let template: HtmlElementAst = <HtmlElementAst>ul.children[0];
expect(template.sourceSpan.start.col).toEqual(26);
expect(template.sourceSpan.end.col).toEqual(41);
let switchCheck = template.attrs[0];
expect(switchCheck.sourceSpan.start.col).toEqual(26);
expect(switchCheck.sourceSpan.end.col).toEqual(28);
let li: HtmlElementAst = <HtmlElementAst>template.children[0];
expect(li.sourceSpan.start.col).toEqual(26);
expect(li.sourceSpan.end.col).toEqual(41);
let b: HtmlElementAst = <HtmlElementAst>li.children[0];
expect(b.sourceSpan.start.col).toEqual(29);
expect(b.sourceSpan.end.col).toEqual(32);
});
it("should handle other special forms", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('m', "gender_male", null))] = 'M';
let res = parse(`{person.gender, gender,=male {m}}`, translations);
expect(humanizeDom(res))
.toEqual([
[HtmlElementAst, 'ul', 0],
[HtmlAttrAst, '[ngSwitch]', 'person.gender'],
[HtmlElementAst, 'template', 1],
[HtmlAttrAst, 'ngSwitchWhen', 'male'],
[HtmlElementAst, 'li', 2],
[HtmlTextAst, 'M', 3],
]);
});
describe("errors", () => {
it("should error when giving an invalid template", () => {
expect(humanizeErrors(parse("<a>a</b>", {}).errors))
.toEqual(['Unexpected closing tag "b"']);
});
it("should error when no matching message (attr)", () => {
let mid = id(new Message("some message", null, null));
expect(humanizeErrors(parse("<div value='some message' i18n-value></div>", {}).errors))
.toEqual([`Cannot find message for id '${mid}', content 'some message'.`]);
});
it("should error when no matching message (text)", () => {
let mid = id(new Message("some message", null, null));
expect(humanizeErrors(parse("<div i18n>some message</div>", {}).errors))
.toEqual([`Cannot find message for id '${mid}', content 'some message'.`]);
});
it("should error when a non-placeholder element appears in translation", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message("some message", null, null))] = "<a>a</a>";
expect(humanizeErrors(parse("<div i18n>some message</div>", translations).errors))
.toEqual([`Unexpected tag "a". Only "ph" tags are allowed.`]);
});
it("should error when a placeholder element does not have the name attribute", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message("some message", null, null))] = "<ph>a</ph>";
expect(humanizeErrors(parse("<div i18n>some message</div>", translations).errors))
.toEqual([`Missing "name" attribute.`]);
});
it("should error when the translation refers to an invalid expression", () => {
let translations: {[key: string]: string} = {};
translations[id(new Message('hi <ph name="0"/>', null, null))] = 'hi <ph name="99"/>';
expect(
humanizeErrors(parse("<div value='hi {{a}}' i18n-value></div>", translations).errors))
.toEqual(["Invalid interpolation name '99'"]);
});
});
});
}
function humanizeErrors(errors: ParseError[]): string[] {
return errors.map(error => error.msg);
}

View File

@ -1,237 +0,0 @@
import {
beforeEach,
describe,
ddescribe,
expect,
iit,
inject,
it,
xdescribe,
xit
} from '@angular/core/testing/testing_internal';
import {HtmlParser} from '@angular/compiler/src/html_parser';
import {MessageExtractor, removeDuplicates} from '@angular/compiler/src/i18n/message_extractor';
import {Message} from '@angular/compiler/src/i18n/message';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
export function main() {
describe('MessageExtractor', () => {
let extractor: MessageExtractor;
beforeEach(() => {
let htmlParser = new HtmlParser();
var parser = new Parser(new Lexer());
extractor = new MessageExtractor(htmlParser, parser);
});
it('should extract from elements with the i18n attr', () => {
let res = extractor.extract("<div i18n='meaning|desc'>message</div>", "someurl");
expect(res.messages).toEqual([new Message("message", 'meaning', 'desc')]);
});
it('should extract from elements with the i18n attr without a desc', () => {
let res = extractor.extract("<div i18n='meaning'>message</div>", "someurl");
expect(res.messages).toEqual([new Message("message", 'meaning', null)]);
});
it('should extract from elements with the i18n attr without a meaning', () => {
let res = extractor.extract("<div i18n>message</div>", "someurl");
expect(res.messages).toEqual([new Message("message", null, null)]);
});
it('should extract from attributes', () => {
let res = extractor.extract(`
<div
title1='message1' i18n-title1='meaning1|desc1'
title2='message2' i18n-title2='meaning2|desc2'>
</div>
`,
"someurl");
expect(res.messages)
.toEqual([
new Message("message1", "meaning1", "desc1"),
new Message("message2", "meaning2", "desc2")
]);
});
it('should extract from partitions', () => {
let res = extractor.extract(`
<!-- i18n: meaning1|desc1 -->message1<!-- /i18n -->
<!-- i18n: meaning2|desc2 -->message2<!-- /i18n -->`,
"someUrl");
expect(res.messages)
.toEqual([
new Message("message1", "meaning1", "desc1"),
new Message("message2", "meaning2", "desc2")
]);
});
it('should ignore other comments', () => {
let res = extractor.extract(`
<!-- i18n: meaning1|desc1 --><!-- other -->message1<!-- /i18n -->`,
"someUrl");
expect(res.messages).toEqual([new Message("message1", "meaning1", "desc1")]);
});
it('should replace interpolation with placeholders (text nodes)', () => {
let res = extractor.extract("<div i18n>Hi {{one}} and {{two}}</div>", "someurl");
expect(res.messages)
.toEqual(
[new Message('<ph name="t0">Hi <ph name="0"/> and <ph name="1"/></ph>', null, null)]);
});
it('should replace interpolation with placeholders (attributes)', () => {
let res =
extractor.extract("<div title='Hi {{one}} and {{two}}' i18n-title></div>", "someurl");
expect(res.messages)
.toEqual([new Message('Hi <ph name="0"/> and <ph name="1"/>', null, null)]);
});
it('should replace interpolation with named placeholders if provided (text nodes)', () => {
let res = extractor.extract(`
<div i18n>Hi {{one //i18n(ph="FIRST")}} and {{two //i18n(ph="SECOND")}}</div>`,
'someurl');
expect(res.messages)
.toEqual([
new Message('<ph name="t0">Hi <ph name="FIRST"/> and <ph name="SECOND"/></ph>', null,
null)
]);
});
it('should replace interpolation with named placeholders if provided (attributes)', () => {
let res = extractor.extract(`
<div title='Hi {{one //i18n(ph="FIRST")}} and {{two //i18n(ph="SECOND")}}'
i18n-title></div>`,
'someurl');
expect(res.messages)
.toEqual([new Message('Hi <ph name="FIRST"/> and <ph name="SECOND"/>', null, null)]);
});
it('should match named placeholders with extra spacing', () => {
let res = extractor.extract(`
<div title='Hi {{one // i18n ( ph = "FIRST" )}} and {{two // i18n ( ph = "SECOND" )}}'
i18n-title></div>`,
'someurl');
expect(res.messages)
.toEqual([new Message('Hi <ph name="FIRST"/> and <ph name="SECOND"/>', null, null)]);
});
it('should suffix duplicate placeholder names with numbers', () => {
let res = extractor.extract(`
<div title='Hi {{one //i18n(ph="FIRST")}} and {{two //i18n(ph="FIRST")}} and {{three //i18n(ph="FIRST")}}'
i18n-title></div>`,
'someurl');
expect(res.messages)
.toEqual([
new Message('Hi <ph name="FIRST"/> and <ph name="FIRST_1"/> and <ph name="FIRST_2"/>',
null, null)
]);
});
it("should handle html content", () => {
let res = extractor.extract(
'<div i18n><div attr="value">zero<div>one</div></div><div>two</div></div>', "someurl");
expect(res.messages)
.toEqual([
new Message('<ph name="e0">zero<ph name="e2">one</ph></ph><ph name="e4">two</ph>', null,
null)
]);
});
it("should handle html content with interpolation", () => {
let res =
extractor.extract('<div i18n><div>zero{{a}}<div>{{b}}</div></div></div>', "someurl");
expect(res.messages)
.toEqual([
new Message(
'<ph name="e0"><ph name="t1">zero<ph name="0"/></ph><ph name="e2"><ph name="t3"><ph name="0"/></ph></ph></ph>',
null, null)
]);
});
it("should extract from nested elements", () => {
let res = extractor.extract(
'<div title="message1" i18n-title="meaning1|desc1"><div i18n="meaning2|desc2">message2</div></div>',
"someurl");
expect(res.messages)
.toEqual([
new Message("message2", "meaning2", "desc2"),
new Message("message1", "meaning1", "desc1")
]);
});
it("should extract messages from attributes in i18n blocks", () => {
let res = extractor.extract(
'<div i18n><div attr="value" i18n-attr="meaning|desc">message</div></div>', "someurl");
expect(res.messages)
.toEqual([
new Message('<ph name="e0">message</ph>', null, null),
new Message('value', "meaning", "desc")
]);
});
it("should extract messages from special forms", () => {
let res = extractor.extract(`
<div>
{messages.length, plural,
=0 {You have <b>no</b> messages}
=1 {You have one message}
}
</div>
`,
"someurl");
expect(res.messages)
.toEqual([
new Message('You have <ph name="e1">no</ph> messages', "plural_0", null),
new Message('You have one message', "plural_1", null)
]);
});
it("should remove duplicate messages", () => {
let res = extractor.extract(`
<!-- i18n: meaning|desc1 -->message<!-- /i18n -->
<!-- i18n: meaning|desc2 -->message<!-- /i18n -->`,
"someUrl");
expect(removeDuplicates(res.messages))
.toEqual([
new Message("message", "meaning", "desc1"),
]);
});
describe("errors", () => {
it('should error on i18n attributes without matching "real" attributes', () => {
let res = extractor.extract(`
<div
title1='message1' i18n-title1='meaning1|desc1' i18n-title2='meaning2|desc2'>
</div>
`,
"someurl");
expect(res.errors.length).toEqual(1);
expect(res.errors[0].msg).toEqual("Missing attribute 'title2'.");
});
it('should error when cannot find a matching desc', () => {
let res = extractor.extract(`
<!-- i18n: meaning1|desc1 -->message1`,
"someUrl");
expect(res.errors.length).toEqual(1);
expect(res.errors[0].msg).toEqual("Missing closing 'i18n' comment.");
});
it("should return parse errors when the template is invalid", () => {
let res = extractor.extract("<input&#Besfs", "someurl");
expect(res.errors.length).toEqual(1);
expect(res.errors[0].msg).toEqual('Unexpected character "s"');
});
});
});
}

View File

@ -1,26 +0,0 @@
import {
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xdescribe,
xit
} from '@angular/core/testing/testing_internal';
import {Message, id} from '@angular/compiler/src/i18n/message';
export function main() {
describe('Message', () => {
describe("id", () => {
it("should return a different id for messages with and without the meaning", () => {
let m1 = new Message("content", "meaning", null);
let m2 = new Message("content", null, null);
expect(id(m1)).toEqual(id(m1));
expect(id(m1)).not.toEqual(id(m2));
});
});
});
}

View File

@ -1,116 +0,0 @@
import {
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xdescribe,
xit
} from '@angular/core/testing/testing_internal';
import {HtmlAst} from '@angular/compiler/src/html_ast';
import {Message, id} from '@angular/compiler/src/i18n/message';
import {serializeXmb, deserializeXmb} from '@angular/compiler/src/i18n/xmb_serializer';
import {ParseSourceSpan, ParseError} from '@angular/compiler/src/parse_util';
export function main() {
describe("Xmb", () => {
describe('Xmb Serialization', () => {
it("should return an empty message bundle for an empty list of messages",
() => { expect(serializeXmb([])).toEqual("<message-bundle></message-bundle>"); });
it("should serializeXmb messages without desc", () => {
let m = new Message("content", "meaning", null);
let expected = `<message-bundle><msg id='${id(m)}'>content</msg></message-bundle>`;
expect(serializeXmb([m])).toEqual(expected);
});
it("should serializeXmb messages with desc", () => {
let m = new Message("content", "meaning", "description");
let expected =
`<message-bundle><msg id='${id(m)}' desc='description'>content</msg></message-bundle>`;
expect(serializeXmb([m])).toEqual(expected);
});
});
describe("Xmb Deserialization", () => {
it("should parse an empty bundle", () => {
let mb = "<message-bundle></message-bundle>";
expect(deserializeXmb(mb, "url").messages).toEqual({});
});
it("should parse an non-empty bundle", () => {
let mb = `
<message-bundle>
<msg id="id1" desc="description1">content1</msg>
<msg id="id2">content2</msg>
</message-bundle>
`;
let parsed = deserializeXmb(mb, "url").messages;
expect(_serialize(parsed["id1"])).toEqual("content1");
expect(_serialize(parsed["id2"])).toEqual("content2");
});
it("should error when cannot parse the content", () => {
let mb = `
<message-bundle>
<msg id="id1" desc="description1">content
</message-bundle>
`;
let res = deserializeXmb(mb, "url");
expect(_serializeErrors(res.errors)).toEqual(['Unexpected closing tag "message-bundle"']);
});
it("should error when cannot find the id attribute", () => {
let mb = `
<message-bundle>
<msg>content</msg>
</message-bundle>
`;
let res = deserializeXmb(mb, "url");
expect(_serializeErrors(res.errors)).toEqual(['"id" attribute is missing']);
});
it("should error on empty content", () => {
let mb = ``;
let res = deserializeXmb(mb, "url");
expect(_serializeErrors(res.errors)).toEqual(['Missing element "message-bundle"']);
});
it("should error on an invalid element", () => {
let mb = `
<message-bundle>
<invalid>content</invalid>
</message-bundle>
`;
let res = deserializeXmb(mb, "url");
expect(_serializeErrors(res.errors)).toEqual(['Unexpected element "invalid"']);
});
it("should expand 'ph' elements", () => {
let mb = `
<message-bundle>
<msg id="id1">a<ph name="i0"/></msg>
</message-bundle>
`;
let res = deserializeXmb(mb, "url").messages["id1"];
expect((<any>res[1]).name).toEqual("ph");
});
});
});
}
function _serialize(nodes: HtmlAst[]): string {
return (<any>nodes[0]).value;
}
function _serializeErrors(errors: ParseError[]): string[] {
return errors.map(e => e.msg);
}

View File

@ -1,9 +0,0 @@
library angular2.test.compiler.metadata_resolver_fixture;
import "package:angular2/core.dart" show Component;
// This component is not actually malformed; this fixture is here to
// make Dart not complain about a missing import for a test case that only
// matters in an JavaScript app.
@Component(template: "")
class MalformedStylesComponent {}

View File

@ -1,5 +0,0 @@
import {Component} from '@angular/core';
@Component({styles:<any>('foo'), template: ''})
export class MalformedStylesComponent {
}

View File

@ -1,25 +0,0 @@
// ATTENTION: This file will be overwritten with generated code by main()
import {print, IS_DART} from '../src/facade/lang';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {DartEmitter} from '@angular/compiler/src/output/dart_emitter';
import {DartImportGenerator} from '@angular/compiler/src/output/dart_imports';
import * as o from '@angular/compiler/src/output/output_ast';
import {compileComp, compAMetadata} from './offline_compiler_util';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import {CompA, SimpleJsImportGenerator} from './offline_compiler_util';
export const CompANgFactory: ComponentFactory<CompA> = null;
export function emit(): Promise<string> {
var emitter = IS_DART ? new DartEmitter(new DartImportGenerator()) :
new TypeScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
// Generator
export function main(args: string[]) {
emit().then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -1,21 +0,0 @@
// ATTENTION: This file will be overwritten with generated code by main()
import {print} from '../src/facade/lang';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {compileComp, compAMetadata} from './offline_compiler_util';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import {CompA, SimpleJsImportGenerator} from './offline_compiler_util';
export const CompANgFactory: ComponentFactory<CompA> = null;
export function emit() {
var emitter = new JavaScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
// Generator
export function main(args: string[]) {
emit().then((source) => {
// debug: console.error(source);
print(source);
});
}

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