diff --git a/gulpfile.js b/gulpfile.js index f4bc5bdf81..2f29335e1a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,4 @@ +var format = require('gulp-clang-format'); var gulp = require('gulp'); var gulpPlugins = require('gulp-load-plugins')(); var shell = require('gulp-shell'); @@ -7,7 +8,6 @@ var merge = require('merge'); var path = require('path'); var gulpTraceur = require('./tools/transpiler/gulp-traceur'); - var clean = require('./tools/build/clean'); var transpile = require('./tools/build/transpile'); var html = require('./tools/build/html'); @@ -143,7 +143,8 @@ var CONFIG = { }), cjs: merge(true, _COMPILER_CONFIG_JS_DEFAULT, { typeAssertionModule: 'rtts_assert/rtts_assert', - typeAssertions: true, + // Don't use type assertions since this is partly transpiled by typescript + typeAssertions: false, modules: 'commonjs' }) }, @@ -296,6 +297,26 @@ gulp.task('build/clean.docs', clean(gulp, gulpPlugins, { // ------------ // transpile +gulp.task('build/transpile.ts.cjs', function() { + var tsResult = gulp.src(CONFIG.transpile.src.ts) + .pipe(sourcemaps.init()) + .pipe(tsc({ + target: 'ES5', + module: /*system.js*/'commonjs', + allowNonTsExtensions: false, + typescript: require('typescript'), + //declarationFiles: true, + noEmitOnError: true + })); + var dest = gulp.dest(CONFIG.dest.js.cjs); + return merge([ + // Write external sourcemap next to the js file + tsResult.js.pipe(sourcemaps.write('.')).pipe(dest), + tsResult.js.pipe(dest), + tsResult.dts.pipe(dest), + ]); +}); + gulp.task('build/transpile.js.dev.es6', transpile(gulp, gulpPlugins, { src: CONFIG.transpile.src.js, dest: CONFIG.dest.js.dev.es6, @@ -314,8 +335,7 @@ gulp.task('build/transpile.ts.dev.es5', function() { module: 'commonjs', typescript: require('typescript'), noEmitOnError: true - })) - .js; + })); return merge([ tsResult.js.pipe(sourcemaps.write('.')) .pipe(gulp.dest(CONFIG.dest.js.dev.es5)), @@ -524,7 +544,7 @@ gulp.task('build/pubbuild.dart', pubbuild(gulp, gulpPlugins, { })); // ------------ -// format dart +// formatting gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, { pub: DART_SDK.PUB, @@ -532,6 +552,11 @@ gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, { args: CONFIG.formatDart.args })); +gulp.task('check-format', function() { + return gulp.src(['modules/**/*.ts', '!**/typings/**/*.d.ts']) + .pipe(format.checkFormat('file')); +}); + // ------------ // check circular dependencies in Node.js context gulp.task('build/checkCircularDependencies', function (done) { @@ -786,6 +811,9 @@ gulp.task('build.js.prod', function(done) { gulp.task('build.js.cjs', function(done) { runSequence( ['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'], + // Overwrite the .js.cjs transpilation with typescript outputs + // We still need traceur outputs everywhere else, for now. + 'build/transpile.ts.cjs', ['build/linknodemodules.js.cjs'], 'build/transformCJSTests', done diff --git a/modules/angular2/globals.ts b/modules/angular2/globals.ts new file mode 100644 index 0000000000..162fc00425 --- /dev/null +++ b/modules/angular2/globals.ts @@ -0,0 +1,23 @@ +/** + * This file contains declarations of global symbols we reference in our code + */ + +declare var assert: any; +declare var global: Window; +type int = number; + +interface List extends Array {} + +interface Window { + Object: typeof Object; + Array: typeof Array; + Map: typeof Map; + Set: typeof Set; + Date: typeof Date; + RegExp: typeof RegExp; + JSON: typeof JSON; + Math: typeof Math; + assert: typeof assert; + NaN: typeof NaN; + gc(): void; +} diff --git a/modules/angular2/src/core/compiler/pipeline/compile_element.js b/modules/angular2/src/core/compiler/pipeline/compile_element.js index 0ffc08b1fc..b66083b94a 100644 --- a/modules/angular2/src/core/compiler/pipeline/compile_element.js +++ b/modules/angular2/src/core/compiler/pipeline/compile_element.js @@ -76,7 +76,7 @@ export class CompileElement { this.ignoreBindings = false; this.contentTagSelector = null; // description is calculated here as compilation steps may change the element - var tplDesc = assertionsEnabled()? getElementDescription(element) : null; + var tplDesc = getElementDescription(element); if (compilationUnit !== '') { this.elementDescription = compilationUnit; if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc; diff --git a/modules/angular2/src/facade/async.ts b/modules/angular2/src/facade/async.ts new file mode 100644 index 0000000000..c7bdbf9797 --- /dev/null +++ b/modules/angular2/src/facade/async.ts @@ -0,0 +1,83 @@ +/// +/// + +// HACK: workaround for Traceur behavior. +// It expects all transpiled modules to contain this marker. +// TODO: remove this when we no longer use traceur +export var __esModule = true; + +import {int, global, isPresent} from 'angular2/src/facade/lang'; +import {List} from 'angular2/src/facade/collection'; +import * as Rx from 'rx'; + +export class PromiseWrapper { + static resolve(obj): Promise { return Promise.resolve(obj); } + + static reject(obj): Promise { return Promise.reject(obj); } + + // Note: We can't rename this method into `catch`, as this is not a valid + // method name in Dart. + static catchError(promise: Promise, onError: (error: any) => T | Thenable): Promise { + return promise.catch(onError); + } + + static all(promises: List>): Promise { + if (promises.length == 0) return Promise.resolve([]); + return Promise.all(promises); + } + + static then(promise: Promise, success: (value: any) => T | Thenable, + rejection: (error: any) => T | Thenable): Promise { + return promise.then(success, rejection); + } + + static completer() { + var resolve; + var reject; + + var p = new Promise(function(res, rej) { + resolve = res; + reject = rej; + }); + + return {promise: p, resolve: resolve, reject: reject}; + } + + static setTimeout(fn: Function, millis: int) { global.setTimeout(fn, millis); } + + static isPromise(maybePromise): boolean { return maybePromise instanceof Promise; } +} + + +/** + * Use Rx.Observable but provides an adapter to make it work as specified here: + * https://github.com/jhusain/observable-spec + * + * Once a reference implementation of the spec is available, switch to it. + */ +type Observable = Rx.Observable; +type ObservableController = Rx.Subject; + +export class ObservableWrapper { + static createController(): Rx.Subject { return new Rx.Subject(); } + + static createObservable(subject: Rx.Subject): Rx.Observable { return subject; } + + static subscribe(observable: Rx.Observable, generatorOrOnNext, onThrow = null, + onReturn = null) { + if (isPresent(generatorOrOnNext.next)) { + return observable.observeOn(Rx.Scheduler.timeout) + .subscribe((value) => generatorOrOnNext.next(value), + (error) => generatorOrOnNext.throw(error), () => generatorOrOnNext.return ()); + } else { + return observable.observeOn(Rx.Scheduler.timeout) + .subscribe(generatorOrOnNext, onThrow, onReturn); + } + } + + static callNext(subject: Rx.Subject, value: any) { subject.onNext(value); } + + static callThrow(subject: Rx.Subject, error: any) { subject.onError(error); } + + static callReturn(subject: Rx.Subject) { subject.onCompleted(); } +} diff --git a/modules/angular2/src/facade/browser.ts b/modules/angular2/src/facade/browser.ts new file mode 100644 index 0000000000..69645e0266 --- /dev/null +++ b/modules/angular2/src/facade/browser.ts @@ -0,0 +1,14 @@ +/** + * JS version of browser APIs. This library can only run in the browser. + */ +// HACK: workaround for Traceur behavior. +// It expects all transpiled modules to contain this marker. +// TODO: remove this when we no longer use traceur +export var __esModule = true; + +var win = window; + +export {win as window}; +export var document = window.document; +export var location = window.location; +export var gc = window.gc ? () => window.gc() : () => null; diff --git a/modules/angular2/src/facade/collection.ts b/modules/angular2/src/facade/collection.ts new file mode 100644 index 0000000000..a65debc39d --- /dev/null +++ b/modules/angular2/src/facade/collection.ts @@ -0,0 +1,198 @@ +import {int, isJsObject, global} from 'angular2/src/facade/lang'; + +// HACK: workaround for Traceur behavior. +// It expects all transpiled modules to contain this marker. +// TODO: remove this when we no longer use traceur +export var __esModule = true; + +export var List = global.Array; +export var Map = global.Map; +export var Set = global.Set; +export var StringMap = global.Object; + +export class MapWrapper { + static create(): Map { return new Map(); } + static clone(m: Map): Map { return new Map(m); } + static createFromStringMap(stringMap): Map { + var result = MapWrapper.create(); + for (var prop in stringMap) { + MapWrapper.set(result, prop, stringMap[prop]); + } + return result; + } + static createFromPairs(pairs: List): Map { return new Map(pairs); } + static get(m, k) { return m.get(k); } + static set(m, k, v) { m.set(k, v); } + static contains(m, k) { return m.has(k); } + static forEach(m, fn) { m.forEach(fn); } + static size(m) { return m.size; } + static delete (m, k) { m.delete(k); } + static clear(m) { m.clear(); } + static clearValues(m) { + var keyIterator = m.keys(); + var k; + while (!((k = keyIterator.next()).done)) { + m.set(k.value, null); + } + } + static iterable(m) { return m; } + static keys(m) { return m.keys(); } + static values(m) { return m.values(); } +} + +/** + * Wraps Javascript Objects + */ +export class StringMapWrapper { + static create(): Object { + // Note: We are not using Object.create(null) here due to + // performance! + // http://jsperf.com/ng2-object-create-null + return {}; + } + static contains(map, key) { return map.hasOwnProperty(key); } + static get(map, key) { return map.hasOwnProperty(key) ? map[key] : undefined; } + static set(map, key, value) { map[key] = value; } + static isEmpty(map) { + for (var prop in map) { + return false; + } + return true; + } + static delete (map, key) { delete map[key]; } + static forEach(map, callback) { + for (var prop in map) { + if (map.hasOwnProperty(prop)) { + callback(map[prop], prop); + } + } + } + + static merge(m1, m2) { + var m = {}; + + for (var attr in m1) { + if (m1.hasOwnProperty(attr)) { + m[attr] = m1[attr]; + } + } + + for (var attr in m2) { + if (m2.hasOwnProperty(attr)) { + m[attr] = m2[attr]; + } + } + + return m; + } +} + +export class ListWrapper { + static create(): List { return new List(); } + static createFixedSize(size): List { return new List(size); } + static get(m, k) { return m[k]; } + static set(m, k, v) { m[k] = v; } + static clone(array: List) { return array.slice(0); } + static map(array, fn) { return array.map(fn); } + static forEach(array: List, fn: Function) { + for (var i = 0; i < array.length; i++) { + fn(array[i]); + } + } + static push(array, el) { array.push(el); } + static first(array) { + if (!array) return null; + return array[0]; + } + static last(array) { + if (!array || array.length == 0) return null; + return array[array.length - 1]; + } + static find(list: List, pred: Function) { + for (var i = 0; i < list.length; ++i) { + if (pred(list[i])) return list[i]; + } + return null; + } + static reduce(list: List, + fn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, + init: T) { + return list.reduce(fn, init); + } + static filter(array, pred: Function) { return array.filter(pred); } + static any(list: List, pred: Function) { + for (var i = 0; i < list.length; ++i) { + if (pred(list[i])) return true; + } + return false; + } + static contains(list: List, el) { return list.indexOf(el) !== -1; } + static reversed(array) { + var a = ListWrapper.clone(array); + return a.reverse(); + } + static concat(a, b) { return a.concat(b); } + static isList(list) { return Array.isArray(list); } + static insert(list, index: int, value) { list.splice(index, 0, value); } + static removeAt(list, index: int) { + var res = list[index]; + list.splice(index, 1); + return res; + } + static removeAll(list, items) { + for (var i = 0; i < items.length; ++i) { + var index = list.indexOf(items[i]); + list.splice(index, 1); + } + } + static removeLast(list: List): T { return list.pop(); } + static remove(list, el): boolean { + var index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + return true; + } + return false; + } + static clear(list) { list.splice(0, list.length); } + static join(list, s) { return list.join(s); } + static isEmpty(list) { return list.length == 0; } + static fill(list: List, value, start: int = 0, end: int = null) { + list.fill(value, start, end === null ? undefined : end); + } + static equals(a: List, b: List): boolean { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + static slice(l: List, from: int, to: int): List { return l.slice(from, to); } + static sort(l: List, compareFn: (a: T, b: T) => number) { l.sort(compareFn); } +} + +export function isListLikeIterable(obj): boolean { + if (!isJsObject(obj)) return false; + return ListWrapper.isList(obj) || + (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v] + Symbol.iterator in obj); // JS Iterable have a Symbol.iterator prop +} + +export function iterateListLike(obj, fn: Function) { + if (ListWrapper.isList(obj)) { + for (var i = 0; i < obj.length; i++) { + fn(obj[i]); + } + } else { + var iterator = obj[Symbol.iterator](); + var item; + while (!((item = iterator.next()).done)) { + fn(item.value); + } + } +} + +export class SetWrapper { + static createFromList(lst: List): Set { return new Set(lst); } + static has(s: Set, key: T): boolean { return s.has(key); } +} diff --git a/modules/angular2/src/facade/lang.ts b/modules/angular2/src/facade/lang.ts new file mode 100644 index 0000000000..2b3e3d4204 --- /dev/null +++ b/modules/angular2/src/facade/lang.ts @@ -0,0 +1,230 @@ +var _global = typeof window === 'undefined' ? global : window; +export {_global as global}; + +// HACK: workaround for Traceur behavior. +// It expects all transpiled modules to contain this marker. +// TODO: remove this when we no longer use traceur +export var __esModule = true; + +export var Type = Function; +export var Math = _global.Math; +export var Date = _global.Date; + +var assertionsEnabled_ = typeof assert !== 'undefined'; + +var int; +// global assert support, as Dart has it... +// TODO: `assert` calls need to be removed in production code! +if (assertionsEnabled_) { + _global.assert = assert; + // `int` is not a valid JS type + int = assert.define( + 'int', function(value) { return typeof value === 'number' && value % 1 === 0; }); +} else { + int = {}; + _global.assert = function() {}; +} +export {int}; + +export class CONST {} +export class ABSTRACT {} +export class IMPLEMENTS {} + +export function isPresent(obj): boolean { + return obj !== undefined && obj !== null; +} + +export function isBlank(obj): boolean { + return obj === undefined || obj === null; +} + +export function isString(obj): boolean { + return typeof obj === "string"; +} + +export function isFunction(obj): boolean { + return typeof obj === "function"; +} + +export function stringify(token): string { + if (typeof token === 'string') { + return token; + } + + if (token === undefined || token === null) { + return '' + token; + } + + if (token.name) { + return token.name; + } + + return token.toString(); +} + +export class StringWrapper { + static fromCharCode(code: int): string { return String.fromCharCode(code); } + + static charCodeAt(s: string, index: int) { return s.charCodeAt(index); } + + static split(s: string, regExp) { return s.split(regExp); } + + static equals(s: string, s2: string): boolean { return s === s2; } + + static replace(s: string, from: string, replace: string): string { + return s.replace(from, replace); + } + + static replaceAll(s: string, from: RegExp, replace: string): string { + return s.replace(from, replace); + } + + static startsWith(s: string, start: string) { return s.startsWith(start); } + + static substring(s: string, start: int, end: int = null) { + return s.substring(start, end === null ? undefined : end); + } + + static replaceAllMapped(s: string, from: RegExp, cb: Function): string { + return s.replace(from, function(...matches) { + // Remove offset & string from the result array + matches.splice(-2, 2); + // The callback receives match, p1, ..., pn + return cb(matches); + }); + } + + static contains(s: string, substr: string): boolean { return s.indexOf(substr) != -1; } +} + +export class StringJoiner { + constructor(public parts = []) {} + + add(part: string) { this.parts.push(part); } + + toString(): string { return this.parts.join(""); } +} + +export class NumberParseError implements Error { + name: string; + + constructor(public message: string) {} + + toString() { return this.message; } +} + + +export class NumberWrapper { + static toFixed(n: number, fractionDigits: int): string { return n.toFixed(fractionDigits); } + + static equal(a, b): boolean { return a === b; } + + static parseIntAutoRadix(text: string): int { + var result: int = parseInt(text); + if (isNaN(result)) { + throw new NumberParseError("Invalid integer literal when parsing " + text); + } + return result; + } + + static parseInt(text: string, radix: int): int { + if (radix == 10) { + if (/^(\-|\+)?[0-9]+$/.test(text)) { + return parseInt(text, radix); + } + } else if (radix == 16) { + if (/^(\-|\+)?[0-9ABCDEFabcdef]+$/.test(text)) { + return parseInt(text, radix); + } + } else { + var result: int = parseInt(text, radix); + if (!isNaN(result)) { + return result; + } + } + throw new NumberParseError("Invalid integer literal when parsing " + text + " in base " + + radix); + } + + // TODO: NaN is a valid literal but is returned by parseFloat to indicate an error. + static parseFloat(text: string): number { return parseFloat(text); } + + static get NaN(): number { return NaN; } + + static isNaN(value): boolean { return isNaN(value); } + + static isInteger(value): boolean { return Number.isInteger(value); } +} + +export var RegExp = _global.RegExp; + +export class RegExpWrapper { + static create(regExpStr, flags: string = ''): RegExp { + flags = flags.replace(/g/g, ''); + return new _global.RegExp(regExpStr, flags + 'g'); + } + static firstMatch(regExp, input) { + // Reset multimatch regex state + regExp.lastIndex = 0; + return regExp.exec(input); + } + static matcher(regExp, input) { + // Reset regex state for the case + // someone did not loop over all matches + // last time. + regExp.lastIndex = 0; + return {re: regExp, input: input}; + } +} + +export class RegExpMatcherWrapper { + static next(matcher) { return matcher.re.exec(matcher.input); } +} + +export class FunctionWrapper { + static apply(fn: Function, posArgs) { return fn.apply(null, posArgs); } +} + +// No subclass so that we preserve error stack. +export var BaseException = Error; + +// JS has NaN !== NaN +export function looseIdentical(a, b): boolean { + return a === b || typeof a === "number" && typeof b === "number" && isNaN(a) && isNaN(b); +} + +// JS considers NaN is the same as NaN for map Key (while NaN !== NaN otherwise) +// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map +export function getMapKey(value) { + return value; +} + +export function normalizeBlank(obj) { + return isBlank(obj) ? null : obj; +} + +export function isJsObject(o): boolean { + return o !== null && (typeof o === "function" || typeof o === "object"); +} + +export function assertionsEnabled(): boolean { + return assertionsEnabled_; +} + +export function print(obj) { + if (obj instanceof Error) { + console.log(obj.stack); + } else { + console.log(obj); + } +} + +// Can't be all uppercase as our transpiler would think it is a special directive... +export var Json = _global.JSON; + +export class DateWrapper { + static fromMillis(ms) { return new Date(ms); } + static toMillis(date: Date) { return date.getTime(); } + static now() { return new Date(); } + static toJson(date) { return date.toJSON(); } +} diff --git a/modules/angular2/src/facade/math.ts b/modules/angular2/src/facade/math.ts new file mode 100644 index 0000000000..f813fd8d85 --- /dev/null +++ b/modules/angular2/src/facade/math.ts @@ -0,0 +1,9 @@ +import {global} from 'angular2/src/facade/lang'; + +// HACK: workaround for Traceur behavior. +// It expects all transpiled modules to contain this marker. +// TODO: remove this when we no longer use traceur +export var __esModule = true; + +export var Math = global.Math; +export var NaN = global.NaN; diff --git a/modules/angular2/traceur-runtime.d.ts b/modules/angular2/traceur-runtime.d.ts new file mode 100644 index 0000000000..658edf4a01 --- /dev/null +++ b/modules/angular2/traceur-runtime.d.ts @@ -0,0 +1,76 @@ +// Extend the ES5 standard library with some ES6 features we polyfill at runtime +// by loading traceur-runtime.js + +// These are mostly copied from lib.es6.d.ts + +interface String { + /** + * Returns true if the sequence of elements of searchString converted to a String is the + * same as the corresponding elements of this object (converted to a String) starting at + * position. Otherwise returns false. + */ + startsWith(searchString: string, position ?: number): boolean; +} + +interface NumberConstructor { + /** + * Returns true if the value passed is an integer, false otherwise. + * @param number A numeric value. + */ + isInteger(number: number): boolean; +} + +interface Array { + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: T, start ?: number, end ?: number): T[]; +} + +// Copied from lib.dom.d.ts and modified +interface Map { + clear(): void; + delete (key: K): boolean; + forEach(callbackfn: (value: V, index: K, map: Map) => void, thisArg ?: any): void; + get(key: K): V; + has(key: K): boolean; + set(key: K, value: V): Map; + size: number; +} +declare var Map: { + new(): Map; + // alexeagle: PATCHED + new(m: Map): Map; + new(l: List): Map; + prototype: Map; +}; + +interface Set { + add(value: T): Set; + clear(): void; + delete (value: T): boolean; + forEach(callbackfn: (value: T, index: T, set: Set) => void, thisArg ?: any): void; + has(value: T): boolean; + size: number; +} +declare var Set: { + new(): Set; + // alexeagle PATCHED + new(s: Set): Set; + new(l: List): Set; + prototype: Set; +}; + +interface SymbolConstructor { + /** + * A method that returns the default iterator for an object.Called by the semantics of the + * for-of statement. + */ + iterator: symbol; +} +declare var Symbol: SymbolConstructor; diff --git a/package.json b/package.json index bf54797983..655805cb7f 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "glob": "^4.0.6", "gulp": "^3.8.8", "gulp-changed": "^1.0.0", + "gulp-clang-format": "^1.0.3", "gulp-concat": "^2.5.2", "gulp-connect": "~1.0.5", "gulp-jasmine": "^1.0.1", @@ -84,7 +85,7 @@ "temp": "^0.8.1", "ternary-stream": "^1.2.3", "through2": "^0.6.1", - "typescript": "Microsoft/TypeScript#cebe42b81fbeeaf68c8e228dda5e602ab94e151b", + "typescript": "alexeagle/TypeScript#93dbbe2a2d0b42cefd02ac949e4bc8ab6b5b5823", "vinyl": "^0.4.6", "yargs": "2.3.*" }