mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
chore: remove dart source and tooling
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
# TODO(hansl): When we're ready to test for Dart, readd every mention of Dart to this file.
|
||||
# Base the Dart config on the main repo travis.yml file.
|
||||
|
||||
language: node_js
|
||||
sudo: false
|
||||
|
||||
@@ -26,8 +23,6 @@ env:
|
||||
- BROWSER_STACK_USERNAME=angularteam1
|
||||
- BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
- ARCH=linux-x64
|
||||
- DART_DEV_VERSION=latest
|
||||
- DART_STABLE_VERSION=latest
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/angular-material2-build/readyfile
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
@@ -44,7 +39,6 @@ env:
|
||||
- MODE=e2e
|
||||
- MODE=saucelabs_required
|
||||
- MODE=browserstack_required
|
||||
- MODE=dart_required DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=saucelabs_optional
|
||||
- MODE=browserstack_optional
|
||||
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
* In GitHub, send a pull request to `material2:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular Material test suites for JS and Dart to ensure tests are still passing.
|
||||
* Re-run the Angular Material test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull
|
||||
Request):
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Also see our [`Good for community contribution`](https://github.com/angular/mate
|
||||
|
||||
## The goal of Angular Material
|
||||
Our goal is to build a set of high-quality UI components built with Angular 2 and TypeScript,
|
||||
following the Material Design spec, that are consumable by both JavaScript and Dart. These
|
||||
following the Material Design spec. These
|
||||
components will serve as an example of how to write Angular code following best practices.
|
||||
|
||||
### What do we mean by "high-quality"?
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Writing Dart Compatible TypeScript
|
||||
|
||||
When writing TypeScript files in Material 2, it is important to keep in mind that these files will be automatically compiled and verified in Dart. There is valid TypeScript that will work in Angular 2, but will be transpiled to invalid Dart code.
|
||||
|
||||
Because of this, there's certain things to keep in mind.
|
||||
|
||||
## Requirements
|
||||
|
||||
This document refers to the following concepts, but do not explain them:
|
||||
|
||||
* The Dart language: https://www.dartlang.org
|
||||
* The `pub` Dart packages management: https://pub.dartlang.org/
|
||||
* The `angular-cli` toolchain: https://github.com/angular/angular-cli
|
||||
|
||||
## Dart Toolchain
|
||||
|
||||
When running `ng build` (or `ng server` which also builds), a Broccoli tree will automatically compile and format your TypeScript code into Dart. These files live along the JavaScript output of the build process, and also are moved into a special directory structure that is compatible with `pub`.
|
||||
|
||||
For example, let say you have a `md-awesome` component for Material, which contains the `awesome.ts` implementation. This file is contained in the source code as `src/components/awesome/awesome.ts`. Building this component (assuming no specs or `scss`) will result in the following files:
|
||||
|
||||
* `dist/components/awesome/awesome.ts`. The original copy.
|
||||
* `dist/components/awesome/awesome.js`. The Javascript code.
|
||||
* `dist/components/awesome/awesome.js.map`. Sourcemap.
|
||||
* `dist/components/awesome/awesome.dart`. The Dart code, transpiled by `ts2dart` and formatted by `dartfmt`.
|
||||
* `dist/dart/lib/components/awesome/awesome.dart`. The same file, copied from above into a directory structure compatible with `pub`.
|
||||
|
||||
If you have transpilation errors (ie. your TypeScript is invalid in Dart), these will fail the `ng build` step now and will output the errors in the console.
|
||||
|
||||
## Gotchas
|
||||
|
||||
Here's a list of gotchas to keep in mind when writing TypeScript code that will be compiled to Dart:
|
||||
|
||||
* **Unused imports are enforced in Dart.** Importing a file but not using it is an error, not a warning.
|
||||
* **`void` is not a proper type for templates.** This is important when creating `EventEmitter<>` or other templates. Instead of `void`, use `Object` and pass in `null`.
|
||||
* **There's no global execution.** Every executed code need to be in a function.
|
||||
* **Unit tests need to live inside a `main()` function.** Because of the point above, `describe()` is technically executed code and has to live in a function. Additionally, spec files are programs in Dart and as such much have a `void main() {}` function containing the test code.
|
||||
* **Boolean expressions are required to be boolean.** There's no type coercion or truthiness concept in Dart. Code like this: `if (!aNumber) {}` must be refactored to be `if (aNumber != 0) {}`.
|
||||
* **Accessing any platform primitive must be done through a Facade.** For example, Promises or DOM manipulation. Facades are going to be provided on a need-to-have basis.
|
||||
* **Union types are a no-go.** Dart has them on its roadmap, but for now we must avoid them.
|
||||
* **Dart files cannot have the same name as a reserved keyword.** For example, `for.dart`, `switch.dart` or `class.dart` are all invalid names. Since the TypeScript files have the same name when transpiled to Dart, they also have the same restriction.
|
||||
* **Default values need to be constants.** This mean that code like `function myFunction(arg1: string = callToSomething()) {}` will not compile.
|
||||
* **The const keyword must have a const value.** Because of that, we cannot do `const x = a + b;` even if the value of `x`, `a` and `b` will not change.
|
||||
* **Lambdas need to abide to the type required.** Meaning that if a function requires a function that takes one argument, the lambda cannot be `() => {}`. Use `_` for temporary parameters. This is notable in Promises.
|
||||
* **Dynamic return values fat arrows can't return non-primitive types without casting.** For example, a fat arrow that return a promise (or Future) generates a warning if using directly, like so: `promiseA.then(() => promiseB)`. In this case simply using a block works as expected; `promiseA.then(() => { return promiseB; })`.
|
||||
* **Dart does not have [generic methods](http://www.typescriptlang.org/docs/handbook/generics.html)** ([issue](https://github.com/dart-lang/sdk/issues/254)).
|
||||
* **Dart does not have destructuring** but it does have a [proposal](https://github.com/dart-lang/dart_enhancement_proposals/issues/24) for it.
|
||||
* **Dart does not support type aliases.** Code like `type Nop = () => void` will not compile.
|
||||
* **Dart does not support [tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals)**.
|
||||
+8
-72
@@ -1,32 +1,16 @@
|
||||
'use strict';
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Import the require hook. Enables us to require TS files natively.
|
||||
require('ts-node/register');
|
||||
|
||||
const detect = require('./tools/build/dart').detect;
|
||||
|
||||
var mergeTrees = require('broccoli-merge-trees');
|
||||
var Angular2App = require('angular-cli/lib/broccoli/angular2-app');
|
||||
var BroccoliSass = require('broccoli-sass');
|
||||
var broccoliAutoprefixer = require('broccoli-autoprefixer');
|
||||
|
||||
const BroccoliTs2Dart = require('./tools/broccoli/broccoli-ts2dart').default;
|
||||
const BroccoliDartFmt = require('./tools/broccoli/broccoli-dartfmt').default;
|
||||
const mergeTrees = require('broccoli-merge-trees');
|
||||
const BroccoliFunnel = require('broccoli-funnel');
|
||||
|
||||
var autoprefixerOptions = require('./build/autoprefixer-options');
|
||||
|
||||
/**
|
||||
* Almost the same as using a plain string for a tree; but unlike a plain
|
||||
* string, Broccoli won't watch this. Used only internally.
|
||||
*/
|
||||
class UnwatchedTree {
|
||||
constructor(dir) { this.dir = dir; }
|
||||
read() { return this.dir; }
|
||||
cleanup() { /* DoNothing */ }
|
||||
}
|
||||
const Angular2App = require('angular-cli/lib/broccoli/angular2-app');
|
||||
const BroccoliSass = require('broccoli-sass');
|
||||
const broccoliAutoprefixer = require('broccoli-autoprefixer');
|
||||
const autoprefixerOptions = require('./build/autoprefixer-options');
|
||||
|
||||
|
||||
module.exports = function(defaults) {
|
||||
@@ -38,63 +22,15 @@ module.exports = function(defaults) {
|
||||
vendorNpmFiles: []
|
||||
});
|
||||
|
||||
var dartAppTree = getDartTree('src/');
|
||||
return mergeTrees([
|
||||
angularAppTree.toTree(),
|
||||
componentCssTree,
|
||||
mainCssTree,
|
||||
demoAppCssTree,
|
||||
demoCssTree
|
||||
].concat(dartAppTree || []));
|
||||
]);
|
||||
};
|
||||
|
||||
/** Gets the Dart tree - Transpile Dart files and format them afterward. */
|
||||
function getDartTree(root) {
|
||||
const dartSDK = detect();
|
||||
if (!dartSDK) {
|
||||
console.warn('---------------------------------------');
|
||||
console.warn('You do not have the Dart SDK installed.');
|
||||
console.warn('In order to contribute to this repo, please refer to');
|
||||
console.warn('https://github.com/angular/material2/blob/master/CONTRIBUTING.md');
|
||||
console.warn('');
|
||||
console.warn('You can still build and serve the demo app without dart support.');
|
||||
return null;
|
||||
}
|
||||
|
||||
const ts2dart = new BroccoliTs2Dart([root], {
|
||||
generateLibraryName: true,
|
||||
generateSourceMap: false,
|
||||
translateBuiltins: true,
|
||||
basePath: path.join(__dirname, root),
|
||||
typingsRoot: '../typings/browser/ambient/',
|
||||
additionalFiles: [path.join(process.cwd(), root, 'typings.d.ts')]
|
||||
});
|
||||
|
||||
const formatter = new BroccoliDartFmt([ts2dart], { dartSDK });
|
||||
|
||||
const dartSources = new BroccoliFunnel(root, {
|
||||
include: ['**/*.dart'],
|
||||
destDir: 'dart/lib',
|
||||
});
|
||||
|
||||
const allDartFiles = mergeTrees([
|
||||
dartSources,
|
||||
formatter
|
||||
]);
|
||||
|
||||
const pubSpecTree = new BroccoliFunnel(new UnwatchedTree('.'), {
|
||||
files: ['pubspec.yaml'],
|
||||
destDir: 'dart'
|
||||
});
|
||||
|
||||
// Publishes the Dart files and pubspec inside a
|
||||
return mergeTrees([
|
||||
pubSpecTree,
|
||||
new BroccoliFunnel(formatter, { destDir: 'dart/lib' }),
|
||||
allDartFiles
|
||||
]);
|
||||
}
|
||||
|
||||
/** Gets the tree for all of the components' CSS. */
|
||||
function getCssTree(folder) {
|
||||
var srcPath = `src/${folder}/`;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"scripts": {
|
||||
"ci:forbidden-identifiers": "node ./scripts/ci/forbidden-identifiers.js",
|
||||
"build": "ng build",
|
||||
"dartanalyzer": "cd dist/dart && pub install && cd ../.. && ts-node scripts/ci/dart_analyzer",
|
||||
"demo-app": "ng serve",
|
||||
"test": "karma start test/karma.conf.js",
|
||||
"tslint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||
@@ -55,7 +54,6 @@
|
||||
"karma": "^0.13.15",
|
||||
"karma-browserstack-launcher": "^0.1.7",
|
||||
"karma-chrome-launcher": "^0.2.1",
|
||||
"karma-dart": "^0.3.0",
|
||||
"karma-firefox-launcher": "^0.1.7",
|
||||
"karma-jasmine": "^0.3.6",
|
||||
"karma-sauce-launcher": "^0.2.14",
|
||||
@@ -63,7 +61,6 @@
|
||||
"strip-ansi": "^3.0.0",
|
||||
"symlink-or-copy": "^1.0.1",
|
||||
"ts-node": "^0.5.5",
|
||||
"ts2dart": "^0.7.27",
|
||||
"tslint": "^3.5.0",
|
||||
"typescript": "^1.7.5",
|
||||
"typings": "^0.6.8",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
name: material2
|
||||
environment:
|
||||
sdk: '>=1.12.0-dev.5.10 <2.0.0'
|
||||
dependencies:
|
||||
observe: '^0.13.1'
|
||||
angular2: '2.0.0-beta.0'
|
||||
dev_dependencies:
|
||||
guinness: '^0.1.18'
|
||||
intl: '^0.12.4'
|
||||
unittest: '^0.11.5+4'
|
||||
quiver: '^0.21.4'
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/src/common/directives.dart#CORE_DIRECTIVES'
|
||||
entry_points:
|
||||
- lib/demo-app/demo-app.dart
|
||||
@@ -5,8 +5,3 @@ This directory contains scripts that are related to CI only.
|
||||
### `build-and-test.sh`
|
||||
|
||||
The main script. Setup the tests environment, build the files using the `angular-cli` tool and run the tests.
|
||||
|
||||
### `install_dart.sh`
|
||||
|
||||
Only run in Dart environments. Install Dart locally on the CI.
|
||||
|
||||
|
||||
@@ -10,24 +10,14 @@ cd $(dirname $0)/../..
|
||||
source scripts/ci/sources/mode.sh
|
||||
source scripts/ci/sources/tunnel.sh
|
||||
|
||||
# Setup environment.
|
||||
if is_dart; then
|
||||
./scripts/ci/install_dart.sh
|
||||
source scripts/ci/sources/env_dart.sh
|
||||
fi
|
||||
|
||||
start_tunnel
|
||||
npm run build
|
||||
npm run inline-resources
|
||||
echo
|
||||
is_dart && pub install
|
||||
|
||||
wait_for_tunnel
|
||||
if is_lint; then
|
||||
npm run tslint
|
||||
npm run ci:forbidden-identifiers
|
||||
elif is_dart; then
|
||||
npm run dartanalyzer
|
||||
elif is_e2e; then
|
||||
ng serve &
|
||||
sleep 20
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import {analyze} from '../../tools/build/dart_analyzer';
|
||||
import {detect} from '../../tools/build/dart';
|
||||
|
||||
|
||||
const command = detect()['ANALYZER'];
|
||||
|
||||
analyze('dist/dart', command, true)
|
||||
.then((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log('Done.');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -x
|
||||
|
||||
|
||||
if [ -z "${DART_CHANNEL}" -o -z "${DART_VERSION}" -o -z "${ARCH}" ]; then
|
||||
echo "No Dart necessary. Not installing."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
AVAILABLE_DART_VERSION=$(curl "https://storage.googleapis.com/dart-archive/channels/${DART_CHANNEL}/release/${DART_VERSION}/VERSION" | python -c \
|
||||
'import sys, json; print(json.loads(sys.stdin.read())["version"])')
|
||||
|
||||
echo Fetch Dart channel: ${DART_CHANNEL}
|
||||
|
||||
URL_PREFIX=https://storage.googleapis.com/dart-archive/channels/${DART_CHANNEL}/release/${DART_VERSION}
|
||||
DART_SDK_URL="$URL_PREFIX/sdk/dartsdk-$ARCH-release.zip"
|
||||
DARTIUM_URL="$URL_PREFIX/dartium/dartium-$ARCH-release.zip"
|
||||
|
||||
download_and_unzip() {
|
||||
ZIPFILE=${1/*\//}
|
||||
curl -O -L $1 && unzip -q $ZIPFILE && rm $ZIPFILE
|
||||
}
|
||||
|
||||
# TODO: do these downloads in parallel
|
||||
download_and_unzip $DART_SDK_URL
|
||||
download_and_unzip $DARTIUM_URL
|
||||
|
||||
echo Fetched new dart version $(<dart-sdk/version)
|
||||
|
||||
if [[ -n $DARTIUM_URL ]]; then
|
||||
mv dartium-* chromium
|
||||
fi
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e -o pipefail
|
||||
|
||||
if [[ -z $ENV_SET ]]; then
|
||||
export ENV_SET=1
|
||||
unset DART
|
||||
PLATFORM="$(uname -s)"
|
||||
|
||||
case "$PLATFORM" in
|
||||
(Darwin)
|
||||
path=$(readlink ${BASH_SOURCE[0]}||echo './scripts/env.sh')
|
||||
export NGDART_SCRIPT_DIR=$(dirname $path)
|
||||
;;
|
||||
(Linux)
|
||||
export NGDART_SCRIPT_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
|
||||
;;
|
||||
(*)
|
||||
echo Unsupported platform $PLATFORM. Exiting ... >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
export NGDART_BASE_DIR=$(dirname $NGDART_SCRIPT_DIR)
|
||||
|
||||
# Try to find the SDK alongside the dart command first.
|
||||
if [[ -z "$DART_SDK" ]]; then
|
||||
DART=$(which dart) || true
|
||||
if [[ -x "$DART" ]]; then
|
||||
DART_SDK="${DART/dart-sdk\/*/dart-sdk}"
|
||||
if [[ ! -e "$DART_SDK" ]]; then
|
||||
unset DART DART_SDK
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# Fallback: Assume it's alongside the current directory (e.g. Travis).
|
||||
if [[ -z "$DART_SDK" ]]; then
|
||||
DART_SDK="$(pwd)/dart-sdk"
|
||||
fi
|
||||
|
||||
: "${DART:=$DART_SDK/bin/dart}"
|
||||
|
||||
if [[ ! -x "$DART" ]]; then
|
||||
echo Unable to locate the dart binary / SDK. Exiting >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [[ -z "$DARTIUM" ]]; then
|
||||
dartiumRoot="$DART_SDK/../chromium"
|
||||
if [[ -e "$dartiumRoot" ]]; then
|
||||
case "$PLATFORM" in
|
||||
(Linux) export DARTIUM="$dartiumRoot/chrome" ;;
|
||||
(Darwin) export DARTIUM="$dartiumRoot/Chromium.app/Contents/MacOS/Chromium" ;;
|
||||
(*) echo Unsupported platform $PLATFORM. Exiting ... >&2 ; exit 3 ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$PLATFORM" in
|
||||
(Linux) export DART_SDK_LIB_SEARCH_PATH="$DART_SDK" ;;
|
||||
(Darwin) export DART_SDK_LIB_SEARCH_PATH="$DART_SDK/libexec" ;;
|
||||
(*) echo Unsupported platform $PLATFORM. Exiting ... >&2 ; exit 3 ;;
|
||||
esac
|
||||
|
||||
export DART_SDK
|
||||
export DARTSDK
|
||||
export DART
|
||||
export PUB=${PUB:-"$DART_SDK/bin/pub"}
|
||||
if [ -z "$PUB_CACHE" ]; then
|
||||
export PUB_CACHE=$DART_SDK/pub-cache
|
||||
fi
|
||||
export DARTANALYZER=${DARTANALYZER:-"$DART_SDK/bin/dartanalyzer"}
|
||||
export DARTDOC=${DARTDOC:-"$DART_SDK/bin/dartdoc"}
|
||||
export DART_DOCGEN=${DART_DOCGEN:-"$DART_SDK/bin/docgen"}
|
||||
export DART_VM_OPTIONS="--old_gen_heap_size=2048"
|
||||
export DARTIUM_BIN=${DARTIUM_BIN:-"$DARTIUM"}
|
||||
export CHROME_BIN=${CHROME_BIN:-"google-chrome"}
|
||||
export PATH=$PATH:$DART_SDK/bin
|
||||
|
||||
echo '*********'
|
||||
echo '** ENV **'
|
||||
echo '*********'
|
||||
echo DART_SDK=$DART_SDK
|
||||
echo DART_SDK_LIB_SEARCH_PATH=$DART_SDK_LIB_SEARCH_PATH
|
||||
echo DART=$DART
|
||||
echo PUB=$PUB
|
||||
echo DARTANALYZER=$DARTANALYZER
|
||||
echo DARTDOC=$DARTDOC
|
||||
echo DART_DOCGEN=$DART_DOCGEN
|
||||
echo DARTIUM_BIN=$DARTIUM_BIN
|
||||
echo CHROME_BIN=$CHROME_BIN
|
||||
echo PATH=$PATH
|
||||
echo NGDART_BASE_DIR=$NGDART_BASE_DIR
|
||||
echo NGDART_SCRIPT_DIR=$NGDART_SCRIPT_DIR
|
||||
|
||||
fi
|
||||
@@ -1,14 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
source ./scripts/ci/sources/tunnel.sh
|
||||
|
||||
is_dart() {
|
||||
[[ "$MODE" = dart* ]]
|
||||
}
|
||||
|
||||
is_e2e() {
|
||||
[[ "$MODE" = e2e ]]
|
||||
}
|
||||
|
||||
is_lint() {
|
||||
[[ "$MODE" = lint ]]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
echo "Connecting to Sauce Labs"
|
||||
while [ ! -f $BROWSER_PROVIDER_READY_FILE ]; do
|
||||
printf "."
|
||||
#dart2js takes longer than the travis 10 min timeout to complete
|
||||
sleep .5
|
||||
done
|
||||
echo
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Annotation for a @BooleanFieldValue() property.
|
||||
*/
|
||||
class BooleanFieldValue {
|
||||
const BooleanFieldValue();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Annotation for a @OneOf([]) property. For now, this only works in TypeScript.
|
||||
* TODO(hansl): Implement this properly in Dart.
|
||||
*/
|
||||
class OneOf {
|
||||
const OneOf(List<String> values);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import "dart:html";
|
||||
|
||||
CssStyleDeclaration getComputedStyle(element, [String pseudoElement]) {
|
||||
return element.getComputedStyle(pseudoElement);
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
library testing.matchers;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:guinness/guinness.dart' as gns;
|
||||
|
||||
import 'package:angular2/src/platform/dom/dom_adapter.dart' show DOM;
|
||||
|
||||
import 'package:angular2/src/facade/lang.dart' show isString;
|
||||
|
||||
Expect expect(actual, [matcher]) {
|
||||
final expect = new Expect(actual);
|
||||
if (matcher != null) expect.to(matcher);
|
||||
return expect;
|
||||
}
|
||||
|
||||
const _u = const Object();
|
||||
|
||||
bool elementContainsStyle(element, styles) {
|
||||
var allPassed = true;
|
||||
if (isString(styles)) {
|
||||
allPassed = DOM.hasStyle(element, styles);
|
||||
} else {
|
||||
styles.forEach((prop, style) {
|
||||
allPassed = allPassed && DOM.hasStyle(element, prop, style);
|
||||
});
|
||||
}
|
||||
return allPassed;
|
||||
}
|
||||
|
||||
expectErrorMessage(actual, expectedMessage) {
|
||||
expect(actual.toString()).toContain(expectedMessage);
|
||||
}
|
||||
|
||||
expectException(Function actual, expectedMessage) {
|
||||
try {
|
||||
actual();
|
||||
} catch (e, s) {
|
||||
expectErrorMessage(e, expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
class Expect extends gns.Expect {
|
||||
Expect(actual) : super(actual);
|
||||
|
||||
NotExpect get not => new NotExpect(actual);
|
||||
|
||||
void toEqual(expected) => toHaveSameProps(expected);
|
||||
void toContainError(message) => expectErrorMessage(this.actual, message);
|
||||
void toThrowError([message = ""]) => toThrowWith(message: message);
|
||||
void toThrowErrorWith(message) => expectException(this.actual, message);
|
||||
void toBePromise() => gns.guinness.matchers.toBeTrue(actual is Future);
|
||||
void toHaveCssClass(className) =>
|
||||
gns.guinness.matchers.toBeTrue(DOM.hasClass(actual, className));
|
||||
void toHaveCssStyle(styles) {
|
||||
gns.guinness.matchers.toBeTrue(elementContainsStyle(actual, styles));
|
||||
}
|
||||
void toImplement(expected) => toBeA(expected);
|
||||
void toBeNaN() =>
|
||||
gns.guinness.matchers.toBeTrue(double.NAN.compareTo(actual) == 0);
|
||||
void toHaveText(expected) => _expect(elementText(actual), expected);
|
||||
void toHaveBeenCalledWith([a = _u, b = _u, c = _u, d = _u, e = _u, f = _u]) =>
|
||||
_expect(_argsMatch(actual, a, b, c, d, e, f), true,
|
||||
reason: 'method invoked with correct arguments');
|
||||
Function get _expect => gns.guinness.matchers.expect;
|
||||
|
||||
// TODO(tbosch): move this hack into Guinness
|
||||
_argsMatch(spyFn, [a0 = _u, a1 = _u, a2 = _u, a3 = _u, a4 = _u, a5 = _u]) {
|
||||
var calls = spyFn.calls;
|
||||
final toMatch = _takeDefined([a0, a1, a2, a3, a4, a5]);
|
||||
if (calls.isEmpty) {
|
||||
return false;
|
||||
} else {
|
||||
gns.SamePropsMatcher matcher = new gns.SamePropsMatcher(toMatch);
|
||||
for (var i = 0; i < calls.length; i++) {
|
||||
var call = calls[i];
|
||||
// TODO: create a better error message, not just 'Expected: <true> Actual: <false>'.
|
||||
// For hacking this is good:
|
||||
// print(call.positionalArguments);
|
||||
if (matcher.matches(call.positionalArguments, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List _takeDefined(List iter) => iter.takeWhile((_) => _ != _u).toList();
|
||||
}
|
||||
|
||||
class NotExpect extends gns.NotExpect {
|
||||
NotExpect(actual) : super(actual);
|
||||
|
||||
void toEqual(expected) => toHaveSameProps(expected);
|
||||
void toBePromise() => gns.guinness.matchers.toBeFalse(actual is Future);
|
||||
void toHaveCssClass(className) =>
|
||||
gns.guinness.matchers.toBeFalse(DOM.hasClass(actual, className));
|
||||
void toHaveCssStyle(styles) {
|
||||
gns.guinness.matchers.toBeFalse(elementContainsStyle(actual, styles));
|
||||
}
|
||||
void toBeNull() => gns.guinness.matchers.toBeFalse(actual == null);
|
||||
Function get _expect => gns.guinness.matchers.expect;
|
||||
}
|
||||
|
||||
String elementText(n) {
|
||||
hasNodes(n) {
|
||||
var children = DOM.childNodes(n);
|
||||
return children != null && children.length > 0;
|
||||
}
|
||||
|
||||
if (n is Iterable) {
|
||||
return n.map(elementText).join("");
|
||||
}
|
||||
|
||||
if (DOM.isCommentNode(n)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (DOM.isElementNode(n) && DOM.tagName(n) == 'CONTENT') {
|
||||
return elementText(DOM.getDistributedNodes(n));
|
||||
}
|
||||
|
||||
if (DOM.hasShadowRoot(n)) {
|
||||
return elementText(DOM.childNodesAsList(DOM.getShadowRoot(n)));
|
||||
}
|
||||
|
||||
if (hasNodes(n)) {
|
||||
return elementText(DOM.childNodesAsList(n));
|
||||
}
|
||||
|
||||
return DOM.getText(n);
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
library angular2.src.testing.testing_internal;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:guinness/guinness.dart' as gns;
|
||||
export 'package:guinness/guinness.dart'
|
||||
hide
|
||||
Expect,
|
||||
expect,
|
||||
NotExpect,
|
||||
beforeEach,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
SpyObject,
|
||||
SpyFunction;
|
||||
|
||||
export 'matchers.dart' show expect, Expect, NotExpect;
|
||||
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
|
||||
|
||||
import 'package:angular2/src/core/di/provider.dart' show bind;
|
||||
import 'package:angular2/src/facade/collection.dart' show StringMapWrapper;
|
||||
|
||||
import 'test_injector.dart';
|
||||
export 'test_injector.dart' show inject;
|
||||
|
||||
TestInjector _testInjector = getTestInjector();
|
||||
bool _isCurrentTestAsync;
|
||||
Future _currentTestFuture;
|
||||
bool _inIt = false;
|
||||
|
||||
class AsyncTestCompleter {
|
||||
final _completer = new Completer();
|
||||
|
||||
AsyncTestCompleter() {
|
||||
_currentTestFuture = this.future;
|
||||
}
|
||||
|
||||
void done() {
|
||||
_completer.complete();
|
||||
}
|
||||
|
||||
Future get future => _completer.future;
|
||||
}
|
||||
|
||||
void testSetup() {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
// beforeEach configuration:
|
||||
// - Priority 3: clear the bindings before each test,
|
||||
// - Priority 2: collect the bindings before each test, see beforeEachProviders(),
|
||||
// - Priority 1: create the test injector to be used in beforeEach() and it()
|
||||
|
||||
gns.beforeEach(() {
|
||||
_testInjector.reset();
|
||||
_currentTestFuture = null;
|
||||
}, priority: 3);
|
||||
|
||||
var completerProvider = bind(AsyncTestCompleter).toFactory(() {
|
||||
// Mark the test as async when an AsyncTestCompleter is injected in an it(),
|
||||
if (!_inIt) throw 'AsyncTestCompleter can only be injected in an "it()"';
|
||||
_isCurrentTestAsync = true;
|
||||
return new AsyncTestCompleter();
|
||||
});
|
||||
|
||||
gns.beforeEach(() {
|
||||
_isCurrentTestAsync = false;
|
||||
_testInjector.addProviders([completerProvider]);
|
||||
}, priority: 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding default providers defined in test_injector.js.
|
||||
*
|
||||
* The given function must return a list of DI providers.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* beforeEachProviders(() => [
|
||||
* bind(Compiler).toClass(MockCompiler),
|
||||
* bind(SomeToken).toValue(myValue),
|
||||
* ]);
|
||||
*/
|
||||
void beforeEachProviders(Function fn) {
|
||||
gns.beforeEach(() {
|
||||
var providers = fn();
|
||||
if (providers != null) _testInjector.addProviders(providers);
|
||||
}, priority: 2);
|
||||
}
|
||||
|
||||
@Deprecated('using beforeEachProviders instead')
|
||||
void beforeEachBindings(Function fn) {
|
||||
beforeEachProviders(fn);
|
||||
}
|
||||
|
||||
void beforeEach(fn) {
|
||||
if (fn is! FunctionWithParamTokens) fn =
|
||||
new FunctionWithParamTokens([], fn, false);
|
||||
gns.beforeEach(() {
|
||||
_testInjector.execute(fn);
|
||||
});
|
||||
}
|
||||
|
||||
void _it(gnsFn, name, fn) {
|
||||
if (fn is! FunctionWithParamTokens) fn =
|
||||
new FunctionWithParamTokens([], fn, false);
|
||||
gnsFn(name, () {
|
||||
_inIt = true;
|
||||
_testInjector.execute(fn);
|
||||
_inIt = false;
|
||||
if (_isCurrentTestAsync) return _currentTestFuture;
|
||||
});
|
||||
}
|
||||
|
||||
void it(name, fn, [timeOut = null]) {
|
||||
_it(gns.it, name, fn);
|
||||
}
|
||||
|
||||
void iit(name, fn, [timeOut = null]) {
|
||||
_it(gns.iit, name, fn);
|
||||
}
|
||||
|
||||
void xit(name, fn, [timeOut = null]) {
|
||||
_it(gns.xit, name, fn);
|
||||
}
|
||||
|
||||
class SpyFunction extends gns.SpyFunction {
|
||||
SpyFunction(String name) : super(name);
|
||||
|
||||
// TODO: vsavkin move to guinness
|
||||
andReturn(value) {
|
||||
return andCallFake(([a0, a1, a2, a3, a4, a5]) => value);
|
||||
}
|
||||
}
|
||||
|
||||
class SpyObject extends gns.SpyObject {
|
||||
final Map<String, SpyFunction> _spyFuncs = {};
|
||||
|
||||
SpyObject([arg]) {}
|
||||
|
||||
SpyFunction spy(String funcName) =>
|
||||
_spyFuncs.putIfAbsent(funcName, () => new SpyFunction(funcName));
|
||||
|
||||
void prop(String funcName, value) {
|
||||
_spyFuncs
|
||||
.putIfAbsent("get:${funcName}", () => new SpyFunction(funcName))
|
||||
.andReturn(value);
|
||||
}
|
||||
|
||||
static stub([object = null, config = null, overrides = null]) {
|
||||
if (object is! SpyObject) {
|
||||
overrides = config;
|
||||
config = object;
|
||||
object = new SpyObject();
|
||||
}
|
||||
|
||||
var m = StringMapWrapper.merge(config, overrides);
|
||||
StringMapWrapper.forEach(m, (value, key) {
|
||||
object.spy(key).andReturn(value);
|
||||
});
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
bool isInInnerZone() => Zone.current['_innerZone'] == true;
|
||||
@@ -1,536 +0,0 @@
|
||||
library angular.core.facade.dom;
|
||||
|
||||
import 'dart:html';
|
||||
import 'package:angular2/platform/common_dom.dart' show setRootDomAdapter;
|
||||
import 'generic_browser_adapter.dart' show GenericBrowserDomAdapter;
|
||||
import 'package:angular2/src/facade/browser.dart';
|
||||
import 'package:angular2/src/facade/lang.dart' show isBlank, isPresent;
|
||||
import 'dart:js' as js;
|
||||
|
||||
// WARNING: Do not expose outside this class. Parsing HTML using this
|
||||
// sanitizer is a security risk.
|
||||
class _IdentitySanitizer implements NodeTreeSanitizer {
|
||||
void sanitizeTree(Node node) {}
|
||||
}
|
||||
|
||||
final _identitySanitizer = new _IdentitySanitizer();
|
||||
|
||||
final _keyCodeToKeyMap = const {
|
||||
8: 'Backspace',
|
||||
9: 'Tab',
|
||||
12: 'Clear',
|
||||
13: 'Enter',
|
||||
16: 'Shift',
|
||||
17: 'Control',
|
||||
18: 'Alt',
|
||||
19: 'Pause',
|
||||
20: 'CapsLock',
|
||||
27: 'Escape',
|
||||
32: ' ',
|
||||
33: 'PageUp',
|
||||
34: 'PageDown',
|
||||
35: 'End',
|
||||
36: 'Home',
|
||||
37: 'ArrowLeft',
|
||||
38: 'ArrowUp',
|
||||
39: 'ArrowRight',
|
||||
40: 'ArrowDown',
|
||||
45: 'Insert',
|
||||
46: 'Delete',
|
||||
65: 'a',
|
||||
66: 'b',
|
||||
67: 'c',
|
||||
68: 'd',
|
||||
69: 'e',
|
||||
70: 'f',
|
||||
71: 'g',
|
||||
72: 'h',
|
||||
73: 'i',
|
||||
74: 'j',
|
||||
75: 'k',
|
||||
76: 'l',
|
||||
77: 'm',
|
||||
78: 'n',
|
||||
79: 'o',
|
||||
80: 'p',
|
||||
81: 'q',
|
||||
82: 'r',
|
||||
83: 's',
|
||||
84: 't',
|
||||
85: 'u',
|
||||
86: 'v',
|
||||
87: 'w',
|
||||
88: 'x',
|
||||
89: 'y',
|
||||
90: 'z',
|
||||
91: 'OS',
|
||||
93: 'ContextMenu',
|
||||
96: '0',
|
||||
97: '1',
|
||||
98: '2',
|
||||
99: '3',
|
||||
100: '4',
|
||||
101: '5',
|
||||
102: '6',
|
||||
103: '7',
|
||||
104: '8',
|
||||
105: '9',
|
||||
106: '*',
|
||||
107: '+',
|
||||
109: '-',
|
||||
110: '.',
|
||||
111: '/',
|
||||
112: 'F1',
|
||||
113: 'F2',
|
||||
114: 'F3',
|
||||
115: 'F4',
|
||||
116: 'F5',
|
||||
117: 'F6',
|
||||
118: 'F7',
|
||||
119: 'F8',
|
||||
120: 'F9',
|
||||
121: 'F10',
|
||||
122: 'F11',
|
||||
123: 'F12',
|
||||
144: 'NumLock',
|
||||
145: 'ScrollLock'
|
||||
};
|
||||
|
||||
final bool _supportsTemplateElement = () {
|
||||
try {
|
||||
return new TemplateElement().content != null;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}();
|
||||
|
||||
class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||
js.JsFunction _setProperty;
|
||||
js.JsFunction _getProperty;
|
||||
js.JsFunction _hasProperty;
|
||||
Map<String, bool> _hasPropertyCache;
|
||||
BrowserDomAdapter() {
|
||||
_hasPropertyCache = new Map();
|
||||
_setProperty = js.context.callMethod(
|
||||
'eval', ['(function(el, prop, value) { el[prop] = value; })']);
|
||||
_getProperty = js.context
|
||||
.callMethod('eval', ['(function(el, prop) { return el[prop]; })']);
|
||||
_hasProperty = js.context
|
||||
.callMethod('eval', ['(function(el, prop) { return prop in el; })']);
|
||||
}
|
||||
static void makeCurrent() {
|
||||
setRootDomAdapter(new BrowserDomAdapter());
|
||||
}
|
||||
|
||||
bool hasProperty(Element element, String name) {
|
||||
// Always return true as the serverside version html_adapter.dart does so.
|
||||
// TODO: change this once we have schema support.
|
||||
// Note: This nees to kept in sync with html_adapter.dart!
|
||||
return true;
|
||||
}
|
||||
|
||||
void setProperty(Element element, String name, Object value) {
|
||||
var cacheKey = "${element.tagName}.${name}";
|
||||
var hasProperty = this._hasPropertyCache[cacheKey];
|
||||
if (hasProperty == null) {
|
||||
hasProperty = this._hasProperty.apply([element, name]);
|
||||
this._hasPropertyCache[cacheKey] = hasProperty;
|
||||
}
|
||||
if (hasProperty) {
|
||||
_setProperty.apply([element, name, value]);
|
||||
}
|
||||
}
|
||||
|
||||
getProperty(Element element, String name) =>
|
||||
_getProperty.apply([element, name]);
|
||||
|
||||
invoke(Element element, String methodName, List args) =>
|
||||
this.getProperty(element, methodName).apply(args, thisArg: element);
|
||||
|
||||
// TODO(tbosch): move this into a separate environment class once we have it
|
||||
logError(error) {
|
||||
window.console.error(error);
|
||||
}
|
||||
|
||||
log(error) {
|
||||
window.console.log(error);
|
||||
}
|
||||
|
||||
logGroup(error) {
|
||||
window.console.group(error);
|
||||
this.logError(error);
|
||||
}
|
||||
|
||||
logGroupEnd() {
|
||||
window.console.groupEnd();
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> get attrToPropMap => const <String, String>{
|
||||
'class': 'className',
|
||||
'innerHtml': 'innerHTML',
|
||||
'readonly': 'readOnly',
|
||||
'tabindex': 'tabIndex',
|
||||
};
|
||||
|
||||
Element query(String selector) => document.querySelector(selector);
|
||||
|
||||
Element querySelector(el, String selector) => el.querySelector(selector);
|
||||
|
||||
ElementList querySelectorAll(el, String selector) =>
|
||||
el.querySelectorAll(selector);
|
||||
|
||||
void on(EventTarget element, String event, callback(arg)) {
|
||||
// due to https://code.google.com/p/dart/issues/detail?id=17406
|
||||
// addEventListener misses zones so we use element.on.
|
||||
element.on[event].listen(callback);
|
||||
}
|
||||
|
||||
Function onAndCancel(EventTarget element, String event, callback(arg)) {
|
||||
// due to https://code.google.com/p/dart/issues/detail?id=17406
|
||||
// addEventListener misses zones so we use element.on.
|
||||
var subscription = element.on[event].listen(callback);
|
||||
return subscription.cancel;
|
||||
}
|
||||
|
||||
void dispatchEvent(EventTarget el, Event evt) {
|
||||
el.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
MouseEvent createMouseEvent(String eventType) =>
|
||||
new MouseEvent(eventType, canBubble: true);
|
||||
Event createEvent(String eventType) => new Event(eventType, canBubble: true);
|
||||
void preventDefault(Event evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
bool isPrevented(Event evt) {
|
||||
return evt.defaultPrevented;
|
||||
}
|
||||
|
||||
String getInnerHTML(Element el) => el.innerHtml;
|
||||
String getOuterHTML(Element el) => el.outerHtml;
|
||||
void setInnerHTML(Element el, String value) {
|
||||
el.innerHtml = value;
|
||||
}
|
||||
|
||||
String nodeName(Node el) => el.nodeName;
|
||||
String nodeValue(Node el) => el.nodeValue;
|
||||
String type(InputElement el) => el.type;
|
||||
Node content(TemplateElement el) =>
|
||||
_supportsTemplateElement ? el.content : el;
|
||||
Node firstChild(el) => el.firstChild;
|
||||
Node nextSibling(Node el) => el.nextNode;
|
||||
Element parentElement(Node el) => el.parentNode;
|
||||
List<Node> childNodes(Node el) => el.childNodes;
|
||||
List childNodesAsList(Node el) => childNodes(el).toList();
|
||||
void clearNodes(Node el) {
|
||||
el.nodes = const [];
|
||||
}
|
||||
|
||||
void appendChild(Node el, Node node) {
|
||||
el.append(node);
|
||||
}
|
||||
|
||||
void removeChild(el, Node node) {
|
||||
node.remove();
|
||||
}
|
||||
|
||||
void replaceChild(Node el, Node newNode, Node oldNode) {
|
||||
oldNode.replaceWith(newNode);
|
||||
}
|
||||
|
||||
ChildNode remove(ChildNode el) {
|
||||
return el..remove();
|
||||
}
|
||||
|
||||
void insertBefore(Node el, node) {
|
||||
el.parentNode.insertBefore(node, el);
|
||||
}
|
||||
|
||||
void insertAllBefore(Node el, Iterable<Node> nodes) {
|
||||
el.parentNode.insertAllBefore(nodes, el);
|
||||
}
|
||||
|
||||
void insertAfter(Node el, Node node) {
|
||||
el.parentNode.insertBefore(node, el.nextNode);
|
||||
}
|
||||
|
||||
String getText(Node el) => el.text;
|
||||
void setText(Node el, String value) {
|
||||
el.text = value;
|
||||
}
|
||||
|
||||
String getValue(el) => el.value;
|
||||
void setValue(el, String value) {
|
||||
el.value = value;
|
||||
}
|
||||
|
||||
bool getChecked(InputElement el) => el.checked;
|
||||
void setChecked(InputElement el, bool isChecked) {
|
||||
el.checked = isChecked;
|
||||
}
|
||||
|
||||
Comment createComment(String text) {
|
||||
return new Comment(text);
|
||||
}
|
||||
|
||||
TemplateElement createTemplate(String html) {
|
||||
var t = new TemplateElement();
|
||||
// We do not sanitize because templates are part of the application code
|
||||
// not user code.
|
||||
t.setInnerHtml(html, treeSanitizer: _identitySanitizer);
|
||||
return t;
|
||||
}
|
||||
|
||||
Element createElement(String tagName, [HtmlDocument doc = null]) {
|
||||
if (doc == null) doc = document;
|
||||
return doc.createElement(tagName);
|
||||
}
|
||||
|
||||
Element createElementNS(String ns, String tagName, [HtmlDocument doc = null]) {
|
||||
if (doc == null) doc = document;
|
||||
return doc.createElementNS(ns, tagName);
|
||||
}
|
||||
|
||||
Text createTextNode(String text, [HtmlDocument doc = null]) {
|
||||
return new Text(text);
|
||||
}
|
||||
|
||||
createScriptTag(String attrName, String attrValue,
|
||||
[HtmlDocument doc = null]) {
|
||||
if (doc == null) doc = document;
|
||||
var el = doc.createElement('SCRIPT');
|
||||
el.setAttribute(attrName, attrValue);
|
||||
return el;
|
||||
}
|
||||
|
||||
StyleElement createStyleElement(String css, [HtmlDocument doc = null]) {
|
||||
if (doc == null) doc = document;
|
||||
var el = doc.createElement('STYLE');
|
||||
el.text = css;
|
||||
return el;
|
||||
}
|
||||
|
||||
ShadowRoot createShadowRoot(Element el) => el.createShadowRoot();
|
||||
ShadowRoot getShadowRoot(Element el) => el.shadowRoot;
|
||||
Element getHost(Element el) => (el as ShadowRoot).host;
|
||||
clone(Node node) => node.clone(true);
|
||||
List<Node> getElementsByClassName(Element element, String name) =>
|
||||
element.getElementsByClassName(name);
|
||||
List<Node> getElementsByTagName(Element element, String name) =>
|
||||
element.querySelectorAll(name);
|
||||
List<String> classList(Element element) => element.classes.toList();
|
||||
void addClass(Element element, String className) {
|
||||
element.classes.add(className);
|
||||
}
|
||||
|
||||
void removeClass(Element element, String className) {
|
||||
element.classes.remove(className);
|
||||
}
|
||||
|
||||
bool hasClass(Element element, String className) =>
|
||||
element.classes.contains(className);
|
||||
|
||||
void setStyle(Element element, String styleName, String styleValue) {
|
||||
element.style.setProperty(styleName, styleValue);
|
||||
}
|
||||
|
||||
bool hasStyle(Element element, String styleName, [String styleValue]) {
|
||||
var value = this.getStyle(element, styleName);
|
||||
return isPresent(styleValue) ? value == styleValue : value.length > 0;
|
||||
}
|
||||
|
||||
void removeStyle(Element element, String styleName) {
|
||||
element.style.removeProperty(styleName);
|
||||
}
|
||||
|
||||
String getStyle(Element element, String styleName) {
|
||||
return element.style.getPropertyValue(styleName);
|
||||
}
|
||||
|
||||
String tagName(Element element) => element.tagName;
|
||||
|
||||
Map<String, String> attributeMap(Element element) {
|
||||
var result = {};
|
||||
result.addAll(element.attributes);
|
||||
// TODO(tbosch): element.getNamespacedAttributes() somehow does not return the attribute value
|
||||
var xlinkHref = element.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
|
||||
if (xlinkHref != null) {
|
||||
result['xlink:href'] = xlinkHref;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool hasAttribute(Element element, String attribute) =>
|
||||
element.attributes.containsKey(attribute);
|
||||
|
||||
bool hasAttributeNS(Element element, String ns, String attribute) =>
|
||||
element.getAttributeNS(ns, attribute) != null;
|
||||
|
||||
String getAttribute(Element element, String attribute) =>
|
||||
element.getAttribute(attribute);
|
||||
|
||||
String getAttributeNS(Element element, String ns, String attribute) =>
|
||||
element.getAttributeNS(ns, attribute);
|
||||
|
||||
void setAttribute(Element element, String name, String value) {
|
||||
element.setAttribute(name, value);
|
||||
}
|
||||
|
||||
void setAttributeNS(Element element, String ns, String name, String value) {
|
||||
element.setAttributeNS(ns, name, value);
|
||||
}
|
||||
|
||||
void removeAttribute(Element element, String name) {
|
||||
//there is no removeAttribute method as of now in Dart:
|
||||
//https://code.google.com/p/dart/issues/detail?id=19934
|
||||
element.attributes.remove(name);
|
||||
}
|
||||
|
||||
void removeAttributeNS(Element element, String ns, String name) {
|
||||
element.getNamespacedAttributes(ns).remove(name);
|
||||
}
|
||||
|
||||
Node templateAwareRoot(Element el) => el is TemplateElement ? el.content : el;
|
||||
|
||||
HtmlDocument createHtmlDocument() =>
|
||||
document.implementation.createHtmlDocument('fakeTitle');
|
||||
|
||||
HtmlDocument defaultDoc() => document;
|
||||
Rectangle getBoundingClientRect(el) => el.getBoundingClientRect();
|
||||
String getTitle() => document.title;
|
||||
void setTitle(String newTitle) {
|
||||
document.title = newTitle;
|
||||
}
|
||||
|
||||
bool elementMatches(n, String selector) =>
|
||||
n is Element && n.matches(selector);
|
||||
bool isTemplateElement(Element el) => el is TemplateElement;
|
||||
bool isTextNode(Node node) => node.nodeType == Node.TEXT_NODE;
|
||||
bool isCommentNode(Node node) => node.nodeType == Node.COMMENT_NODE;
|
||||
bool isElementNode(Node node) => node.nodeType == Node.ELEMENT_NODE;
|
||||
bool hasShadowRoot(Node node) {
|
||||
return node is Element && node.shadowRoot != null;
|
||||
}
|
||||
|
||||
bool isShadowRoot(Node node) {
|
||||
return node is ShadowRoot;
|
||||
}
|
||||
|
||||
Node importIntoDoc(Node node) {
|
||||
return document.importNode(node, true);
|
||||
}
|
||||
|
||||
Node adoptNode(Node node) {
|
||||
return document.adoptNode(node);
|
||||
}
|
||||
|
||||
String getHref(AnchorElement element) {
|
||||
return element.href;
|
||||
}
|
||||
|
||||
String getEventKey(KeyboardEvent event) {
|
||||
int keyCode = event.keyCode;
|
||||
return _keyCodeToKeyMap.containsKey(keyCode)
|
||||
? _keyCodeToKeyMap[keyCode]
|
||||
: 'Unidentified';
|
||||
}
|
||||
|
||||
getGlobalEventTarget(String target) {
|
||||
if (target == "window") {
|
||||
return window;
|
||||
} else if (target == "document") {
|
||||
return document;
|
||||
} else if (target == "body") {
|
||||
return document.body;
|
||||
}
|
||||
}
|
||||
|
||||
getHistory() {
|
||||
return window.history;
|
||||
}
|
||||
|
||||
getLocation() {
|
||||
return window.location;
|
||||
}
|
||||
|
||||
String getBaseHref() {
|
||||
var href = getBaseElementHref();
|
||||
if (href == null) {
|
||||
return null;
|
||||
}
|
||||
return _relativePath(href);
|
||||
}
|
||||
|
||||
resetBaseElement() {
|
||||
baseElement = null;
|
||||
}
|
||||
|
||||
String getUserAgent() {
|
||||
return window.navigator.userAgent;
|
||||
}
|
||||
|
||||
void setData(Element element, String name, String value) {
|
||||
element.dataset[name] = value;
|
||||
}
|
||||
|
||||
String getData(Element element, String name) {
|
||||
return element.dataset[name];
|
||||
}
|
||||
|
||||
getComputedStyle(elem) => elem.getComputedStyle();
|
||||
|
||||
// TODO(tbosch): move this into a separate environment class once we have it
|
||||
setGlobalVar(String path, value) {
|
||||
var parts = path.split('.');
|
||||
var obj = js.context;
|
||||
while (parts.length > 1) {
|
||||
var name = parts.removeAt(0);
|
||||
if (obj.hasProperty(name)) {
|
||||
obj = obj[name];
|
||||
} else {
|
||||
obj = obj[name] = new js.JsObject(js.context['Object']);
|
||||
}
|
||||
}
|
||||
obj[parts.removeAt(0)] = value;
|
||||
}
|
||||
|
||||
requestAnimationFrame(callback) {
|
||||
return window.requestAnimationFrame(callback);
|
||||
}
|
||||
|
||||
cancelAnimationFrame(id) {
|
||||
window.cancelAnimationFrame(id);
|
||||
}
|
||||
|
||||
num performanceNow() {
|
||||
return window.performance.now();
|
||||
}
|
||||
|
||||
parse(s) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
}
|
||||
|
||||
var baseElement = null;
|
||||
String getBaseElementHref() {
|
||||
if (baseElement == null) {
|
||||
baseElement = document.querySelector('base');
|
||||
if (baseElement == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return baseElement.getAttribute('href');
|
||||
}
|
||||
|
||||
// based on urlUtils.js in AngularJS 1
|
||||
AnchorElement _urlParsingNode = null;
|
||||
String _relativePath(String url) {
|
||||
if (_urlParsingNode == null) {
|
||||
_urlParsingNode = new AnchorElement();
|
||||
}
|
||||
_urlParsingNode.href = url;
|
||||
var pathname = _urlParsingNode.pathname;
|
||||
return (pathname[0] == '/') ? pathname : '/${pathname}';
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import "package:angular2/platform/browser.dart" show bootstrap;
|
||||
import "./demo-app/demo-app.dart" show DemoApp;
|
||||
import "package:angular2/router.dart" show ROUTER_PROVIDERS;
|
||||
import "./core/platform/browser/browser_adapter.dart" show BrowserDomAdapter;
|
||||
|
||||
void main() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
bootstrap(DemoApp, [ROUTER_PROVIDERS]);
|
||||
}
|
||||
@@ -52,9 +52,6 @@ const configuration: { [name: string]: ConfigurationInfo } = {
|
||||
};
|
||||
|
||||
export const customLaunchers: { [name: string]: BrowserLauncherInfo } = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] },
|
||||
'ChromeNoSandbox': {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox'] },
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
/**
|
||||
* This file is Node ES6 and will not be translated to Dart.
|
||||
*/
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Setup TypeScript for our tests.
|
||||
*/
|
||||
/** Setup TypeScript for our tests. */
|
||||
require('ts-node/register');
|
||||
|
||||
const E2E_BASE_URL = process.env['E2E_BASE_URL'] || 'http://localhost:4200';
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/// <reference path="../typings/node/node.d.ts" />
|
||||
/// <reference path="../typings/fs-extra/fs-extra.d.ts" />
|
||||
/// <reference path="./broccoli.d.ts" />
|
||||
|
||||
interface BroccoliCachingWriterPlugin extends BroccoliTree {
|
||||
listFiles(): string[];
|
||||
listEntries(): any[];
|
||||
}
|
||||
|
||||
interface Constructor {
|
||||
new (...args: any[]): BroccoliCachingWriterPlugin;
|
||||
}
|
||||
|
||||
interface CacheEntry {
|
||||
version: number;
|
||||
}
|
||||
|
||||
|
||||
import fs = require('fs');
|
||||
import fse = require('fs-extra');
|
||||
import path = require('path');
|
||||
|
||||
const Plugin: Constructor = require('broccoli-caching-writer');
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
|
||||
const promiseExec = (cmd: string): Promise<void> => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
exec(cmd, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.error(shortenFormatterOutput(stderr));
|
||||
reject('Formatting failed.');
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export default class DartFormatter extends Plugin {
|
||||
private DARTFMT: string;
|
||||
private cache: {[filePath: string]: CacheEntry} = Object.create(null);
|
||||
|
||||
constructor(inputPaths: any[], options: any) {
|
||||
super(inputPaths, options);
|
||||
if (!options.dartSDK) throw new Error("Missing Dart SDK");
|
||||
this.DARTFMT = options.dartSDK.DARTFMT;
|
||||
}
|
||||
|
||||
build(): Promise<any> {
|
||||
const dartEntries = this.listEntries().filter(entry => entry.relativePath.match(/\.dart$/));
|
||||
|
||||
const changedEntries: any[] = dartEntries.map(entry => {
|
||||
const filePath = entry.relativePath;
|
||||
const inputPath = path.join(this.inputPaths[0], filePath);
|
||||
const cachePath = path.join(this.cachePath, filePath);
|
||||
|
||||
if (!this.cache[inputPath]) {
|
||||
this.cache[inputPath] = { version: entry.mtime };
|
||||
} else if (this.cache[inputPath].version >= entry.mtime) {
|
||||
// Ignore this, cache is as young as input.
|
||||
return;
|
||||
}
|
||||
|
||||
fse.copySync(inputPath, cachePath);
|
||||
}).filter(x => !!x);
|
||||
|
||||
return Promise.all(
|
||||
changedEntries.map(entry => {
|
||||
const filePath = entry.relativePath;
|
||||
const cachePath = path.join(this.cachePath, filePath);
|
||||
return promiseExec(this.DARTFMT + ' ' + cachePath);
|
||||
}))
|
||||
.then(() => Promise.all(
|
||||
Object.keys(this.cache).map(filePath => {
|
||||
const cachePath = filePath.replace(this.inputPaths[0], this.cachePath);
|
||||
const outputPath = filePath.replace(this.inputPaths[0], this.outputPath);
|
||||
|
||||
return new Promise(resolve => {
|
||||
fse.mkdirsSync(path.dirname(outputPath));
|
||||
fs.linkSync(cachePath, outputPath);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
var ARROW_LINE = /^(\s+)\^+/;
|
||||
var BEFORE_CHARS = 15;
|
||||
var stripAnsi = require('strip-ansi');
|
||||
function shortenFormatterOutput(formatterOutput) {
|
||||
var lines = formatterOutput.split('\n');
|
||||
var match, line;
|
||||
for (var i = 0; i < lines.length; i += 1) {
|
||||
line = lines[i];
|
||||
if (match = stripAnsi(line).match(ARROW_LINE)) {
|
||||
let leadingWhitespace = match[1].length;
|
||||
let leadingCodeChars = Math.min(leadingWhitespace, BEFORE_CHARS);
|
||||
lines[i] = line.substr(leadingWhitespace - leadingCodeChars);
|
||||
lines[i - 1] = lines[i - 1].substr(leadingWhitespace - leadingCodeChars, 80) + '…';
|
||||
}
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/// <reference path="../typings/node/node.d.ts" />
|
||||
/// <reference path="../typings/fs-extra/fs-extra.d.ts" />
|
||||
/// <reference path="./broccoli.d.ts" />
|
||||
|
||||
interface BroccoliCachingWriterPlugin extends BroccoliTree {
|
||||
listFiles(): string[];
|
||||
listEntries(): any[];
|
||||
}
|
||||
|
||||
interface Constructor {
|
||||
new (...args: any[]): BroccoliCachingWriterPlugin;
|
||||
}
|
||||
|
||||
interface CacheEntry {
|
||||
version: number;
|
||||
}
|
||||
|
||||
|
||||
import fs = require('fs');
|
||||
import fse = require('fs-extra');
|
||||
import path = require('path');
|
||||
|
||||
const Plugin: Constructor = require('broccoli-caching-writer');
|
||||
import TS2Dart = require('ts2dart');
|
||||
|
||||
|
||||
export default class TSToDartTranspiler extends Plugin {
|
||||
private transpiler: TS2Dart.Transpiler;
|
||||
private additionalFiles: string[];
|
||||
private basePath: string;
|
||||
private cache: {[filePath: string]: CacheEntry} = Object.create(null);
|
||||
|
||||
constructor(inputPaths: any[], public options: any /*ts2dart.TranspilerOptions*/) {
|
||||
super(inputPaths, {});
|
||||
|
||||
// Additional files to include in the transpile call. These files could include, e.g.,
|
||||
// .d.ts files. They will always be included in the transpile call below, even if they
|
||||
// weren't modified.
|
||||
this.additionalFiles = options.additionalFiles || [];
|
||||
|
||||
// Delete additionalFiles as the transpiler does not support that option.
|
||||
delete options.additionalFiles;
|
||||
|
||||
this.basePath = options.basePath;
|
||||
this.transpiler = new TS2Dart.Transpiler(options);
|
||||
}
|
||||
|
||||
build(): Promise<any> {
|
||||
const inputPath = this.inputPaths[0];
|
||||
const basePath = this.basePath || inputPath;
|
||||
const getDartFilePath = (path: string) => path.replace(/((\.js)|(\.ts))$/i, '.dart');
|
||||
|
||||
// List all the changed TS files. The `toEmit` variable will contain the list
|
||||
// of relative paths to the basePath of files that have changed.
|
||||
// For an initial run this is basically all the files.
|
||||
const toEmit = this.listEntries()
|
||||
.filter(entry => entry.relativePath.match(/\.ts$/) && !entry.relativePath.match(/\.d\.ts$/))
|
||||
.map(entry => {
|
||||
const fullPath = path.resolve(inputPath, entry.relativePath);
|
||||
const dartInputFilePath = getDartFilePath(fullPath);
|
||||
|
||||
// If a dart file exist, just ignore the TS source.
|
||||
if (fs.existsSync(dartInputFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.cache[fullPath]) {
|
||||
this.cache[fullPath] = { version: entry.mtime };
|
||||
} else if (this.cache[fullPath].version >= entry.mtime) {
|
||||
// Do nothing. Cache is as recent as the original input.
|
||||
return;
|
||||
}
|
||||
return path.normalize(fullPath.replace(inputPath, basePath));
|
||||
})
|
||||
.filter(x => !!x);
|
||||
|
||||
this.transpiler.transpile(this.additionalFiles.concat(toEmit), this.cachePath);
|
||||
|
||||
const dirs: {[p: string]: boolean} = Object.create(null);
|
||||
// Make a symbolic link in the output for the cache.
|
||||
return Promise.all(
|
||||
Object.keys(this.cache).map(filePath => {
|
||||
return new Promise(resolve => {
|
||||
const cachePath = getDartFilePath(filePath.replace(inputPath, this.cachePath));
|
||||
const outputPath = getDartFilePath(filePath.replace(inputPath, this.outputPath));
|
||||
|
||||
const dirPath = path.dirname(outputPath);
|
||||
if (!dirs[dirPath]) {
|
||||
dirs[dirPath] = true;
|
||||
fse.mkdirsSync(path.dirname(outputPath));
|
||||
}
|
||||
fs.linkSync(cachePath, outputPath);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/// <reference path="../typings/node/node.d.ts" />
|
||||
// TODO(hansl): Get rid of this file. It's unneeded.
|
||||
|
||||
const which = require('which');
|
||||
const child_process = require('child_process');
|
||||
|
||||
|
||||
export type DartSdkExecutableMap = {
|
||||
ANALYZER: string,
|
||||
DARTDOCGEN: string,
|
||||
DARTFMT: string,
|
||||
PUB: string,
|
||||
VM: string,
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a map of {EXECUTABLE => Path} for the Dart SDK.
|
||||
* The executables are different on Windows / Unix. Returns null if the Dart SDK cannot be
|
||||
* found.
|
||||
*/
|
||||
export function detect(): DartSdkExecutableMap {
|
||||
let sdk: DartSdkExecutableMap;
|
||||
try {
|
||||
const dartExecutable = which.sync('dart');
|
||||
if (process.platform === 'win32') {
|
||||
sdk = {
|
||||
ANALYZER: 'dartanalyzer.bat',
|
||||
DARTDOCGEN: 'dartdoc.bat',
|
||||
DARTFMT: 'dartfmt.bat',
|
||||
PUB: 'pub.bat',
|
||||
VM: 'dart.exe'
|
||||
};
|
||||
} else {
|
||||
sdk = {
|
||||
ANALYZER: 'dartanalyzer',
|
||||
DARTDOCGEN: 'dartdoc',
|
||||
DARTFMT: 'dartfmt',
|
||||
PUB: 'pub',
|
||||
VM: 'dart'
|
||||
};
|
||||
}
|
||||
console.log('Dart SDK detected:', dartExecutable);
|
||||
console.log('- dart: ' + child_process.spawnSync(sdk.VM, ['--version']).stderr.toString().replace(/\n/g, ''));
|
||||
console.log('- pub: ' + child_process.spawnSync(sdk.PUB, ['--version']).stdout.toString().replace(/\n/g, ''));
|
||||
|
||||
return sdk;
|
||||
} catch (e) {
|
||||
console.log('Dart SDK is not available.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
/// <reference path="../typings/es6-promise/es6-promise.d.ts" />
|
||||
/// <reference path="../typings/node/node.d.ts" />
|
||||
|
||||
import {travisFoldStart} from '../travis/fold';
|
||||
|
||||
import readline = require('readline');
|
||||
import child_process = require('child_process');
|
||||
import path = require('path');
|
||||
import fs = require('fs');
|
||||
|
||||
const glob = require('glob');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
|
||||
const tempFile = '_analyzer.dart';
|
||||
|
||||
|
||||
/**
|
||||
* Call the Dart Analyzer on every dart files below.
|
||||
* Specialized for Travis.
|
||||
*
|
||||
* @param dir The directory to scan.
|
||||
* @param command The command to use for dartanalyzer.
|
||||
* @param use_ddc Enable strong static type checking (see https://goo.gl/DqcBsw).
|
||||
*/
|
||||
export function analyze(dir: string, command: string, use_ddc: boolean = false): Promise<any> {
|
||||
var travisFoldEnd = travisFoldStart(`dartanalyzer-${use_ddc ? 'ddc' : ''}-${dir}`);
|
||||
|
||||
var pubspecContents = fs.readFileSync(path.join(dir, 'pubspec.yaml'));
|
||||
var pubspec = yaml.safeLoad(pubspecContents);
|
||||
var packageName = pubspec.name;
|
||||
|
||||
var libFiles = [].slice.call(glob.sync('lib/**/*.dart', {cwd: dir}));
|
||||
var webFiles = [].slice.call(glob.sync('web/**/*.dart', {cwd: dir}));
|
||||
var testFiles = [].slice.call(glob.sync('test/**/*_spec.dart', {cwd: dir}));
|
||||
|
||||
var analyzeFile = ['library _analyzer;'];
|
||||
libFiles.concat(testFiles).concat(webFiles).forEach((fileName, index) => {
|
||||
if (fileName !== tempFile && fileName.indexOf("/packages/") === -1) {
|
||||
if (fileName.indexOf('lib') == 0) {
|
||||
fileName = 'package:' + packageName + '/' +
|
||||
path.relative('lib', fileName).replace(/\\/g, '/');
|
||||
}
|
||||
analyzeFile.push('import "' + fileName + '" as mod' + index + ';');
|
||||
}
|
||||
});
|
||||
fs.writeFileSync(path.join(dir, tempFile), analyzeFile.join('\n'));
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
_analyze(command, dir, resolve, use_ddc);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function _analyze(command, dirName, done, useDdc) {
|
||||
// TODO remove --package-warnings once dartanalyzer handles transitive libraries
|
||||
var flags = ['--fatal-warnings', '--format=machine'];
|
||||
|
||||
if (useDdc) {
|
||||
console.log('Using DDC analyzer to analyze', dirName);
|
||||
flags.push('--strong');
|
||||
}
|
||||
|
||||
var args = flags.concat(tempFile);
|
||||
|
||||
var stream = child_process.spawn(command, args, {
|
||||
// inherit stdin and stderr, but filter stdout
|
||||
stdio: [process.stdin, process.stdout, 'pipe'],
|
||||
cwd: dirName
|
||||
});
|
||||
// Filter out unused imports from our generated file.
|
||||
// We don't reexports from the generated file
|
||||
// as this could lead to name clashes when two files
|
||||
// export the same thing.
|
||||
var rl =
|
||||
readline.createInterface({input: stream.stderr, output: process.stdout, terminal: false});
|
||||
var hintCount = 0;
|
||||
var errorCount = 0;
|
||||
var warningCount = 0;
|
||||
rl.on('line', function(line) {
|
||||
if (line == "find: > bin [: No such file or directory") {
|
||||
// Skip bad output from Dart SDK .bat files on Windows
|
||||
return;
|
||||
}
|
||||
var parsedLine = _AnalyzerOutputLine.parse(line);
|
||||
if (!parsedLine) {
|
||||
errorCount++;
|
||||
console.log('Unexpected output: ' + line);
|
||||
return;
|
||||
}
|
||||
// TODO remove once dartanalyzer handles transitive libraries
|
||||
// skip errors in third-party packages
|
||||
if (parsedLine.sourcePath.indexOf(dirName) == -1) {
|
||||
return;
|
||||
}
|
||||
if (parsedLine.shouldIgnore()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsedLine.isHint) {
|
||||
hintCount++;
|
||||
} else if (parsedLine.isWarning) {
|
||||
warningCount++;
|
||||
} else {
|
||||
errorCount++;
|
||||
}
|
||||
console.log(dirName + ':' + parsedLine);
|
||||
});
|
||||
stream.on('close', function() {
|
||||
var error;
|
||||
var report = [];
|
||||
if (errorCount > 0) {
|
||||
report.push(errorCount + ' error(s)');
|
||||
}
|
||||
if (warningCount > 0) {
|
||||
report.push(warningCount + ' warning(s)');
|
||||
}
|
||||
if (hintCount > 0) {
|
||||
report.push(hintCount + ' hint(s)');
|
||||
}
|
||||
if (report.length > 0) {
|
||||
error = 'Dartanalyzer showed ' + report.join(', ');
|
||||
}
|
||||
done(error);
|
||||
});
|
||||
stream.on('error', function(error) { done(error); });
|
||||
}
|
||||
|
||||
|
||||
// See https://github.com/dart-lang/analyzer_cli/blob/master/lib/src/error_formatter.dart
|
||||
class _AnalyzerOutputLine {
|
||||
severity: string;
|
||||
errorType: string;
|
||||
errorCode: string;
|
||||
sourcePath: string;
|
||||
lineNum: number;
|
||||
colNum: string;
|
||||
asciiLineLength: number;
|
||||
errorMsg: string;
|
||||
|
||||
isError: boolean;
|
||||
isHint: boolean;
|
||||
isWarning: boolean;
|
||||
|
||||
constructor(result: any[]) {
|
||||
this.severity = result[1];
|
||||
this.errorType = result[2];
|
||||
this.errorCode = result[3];
|
||||
this.sourcePath = result[4];
|
||||
this.lineNum = parseInt(result[5], 10);
|
||||
this.colNum = result[6];
|
||||
this.asciiLineLength = parseInt(result[7], 10);
|
||||
this.errorMsg = result[8];
|
||||
|
||||
this.isError = Boolean(this.severity.match(/ERROR/i));
|
||||
this.isHint = Boolean(this.severity.match(/INFO/i));
|
||||
this.isWarning = Boolean(this.severity.match(/WARNING/i));
|
||||
}
|
||||
|
||||
static parse(line: string) {
|
||||
const result = this._analyzerParseRegExp.exec(line);
|
||||
return result ? new this(result) : null;
|
||||
}
|
||||
|
||||
private static _analyzerParseRegExp =
|
||||
new RegExp('([^\|]+)\\|' + // #1, severity (NONE, INFO, WARNING, ERROR)
|
||||
'([^\|]+)\\|' + // #2, errorCode.type (HINT, *_WARNING, *_ERROR, etc)
|
||||
'([^\|]+)\\|' + // #3, errorCode (UNUSED_IMPORT, UNUSED_CATCH_STACK, etc)
|
||||
'([^\|]+[^\|\\\\])\\|' + // #4, sourcePath with '|' chars backslash-escaped.
|
||||
'([^\|]+)\\|' + // #5, line number
|
||||
'([^\|]+)\\|' + // #6, column number
|
||||
'([^\|]+)\\|' + // #7, length of the ASCII line to draw
|
||||
'(.*)$'); // #8, error message
|
||||
|
||||
/* Maps file path (as string) to file source (an array of strings, one per line). */
|
||||
private static _cache = {};
|
||||
|
||||
private static ERR_NO_SOURCE = '(Could not find source line).';
|
||||
|
||||
toString() {
|
||||
var sourceLine = this._getSourceLine();
|
||||
var lineText = _AnalyzerOutputLine.ERR_NO_SOURCE;
|
||||
if (sourceLine) {
|
||||
var repeat = function(str, num) {
|
||||
if (str.repeat) return str.repeat(num);
|
||||
return Array.prototype.join.call({length: num}, str);
|
||||
};
|
||||
|
||||
lineText =
|
||||
'\n' + sourceLine + '\n' + repeat(' ', this.colNum) + repeat('^', this.asciiLineLength);
|
||||
}
|
||||
return '[' + this.severity + '] type: ' + this.errorType + ' ' +
|
||||
this.errorMsg + ' (' + this.sourcePath + ', line ' + this.lineNum +
|
||||
', col ' + this.colNum + ')' + lineText;
|
||||
}
|
||||
|
||||
shouldIgnore() {
|
||||
if (this.errorCode.match(/UNUSED_IMPORT/i)) {
|
||||
if (this.sourcePath.match(/_analyzer\.dart/)) {
|
||||
return true;
|
||||
}
|
||||
// TODO remove it once ts2dart properly generates abstract getters
|
||||
if (this.errorMsg.match(/unimplemented/)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.errorCode.match(/DEPRECATED_MEMBER_USE/i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: https://github.com/angular/ts2dart/issues/168
|
||||
if (this.errorCode.match(/UNUSED_CATCH_STACK/i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODOs shouldn't break our build...
|
||||
if (this.errorCode.match(/TODO/i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't worry about hints in generated files.
|
||||
if (this.isHint && this.sourcePath.match(/generated/i)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reads the source file for the Analyzer output, caching it for future use.
|
||||
_getSourceLine() {
|
||||
var cache = _AnalyzerOutputLine._cache;
|
||||
var sourceLines = null;
|
||||
if (cache.hasOwnProperty(this.sourcePath)) {
|
||||
sourceLines = cache[this.sourcePath];
|
||||
} else {
|
||||
try {
|
||||
sourceLines = String(fs.readFileSync(this.sourcePath));
|
||||
sourceLines = sourceLines.split('\n');
|
||||
} catch (e) {
|
||||
sourceLines = null;
|
||||
} finally {
|
||||
// Even if this fails, cache `null` so we don't try again.
|
||||
cache[this.sourcePath] = sourceLines;
|
||||
}
|
||||
}
|
||||
return (sourceLines && this.lineNum <= sourceLines.length) ? sourceLines[this.lineNum - 1] : null;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user