Skip to content

Commit

Permalink
fix(typings): Don't expose typing dependencies to users.
Browse files Browse the repository at this point in the history
This resolves Duplicate Identifier issues seen by many users,
at the expense of more typings installation required in some
cases.

Removes the quickstart hack of placing all needed dependencies
typings files in our distribution. Removes dependencies on
nodejs from angular2/core.

Fixes angular#5973
Fixes angular#5807
Fixes angular#6266

Angular now depends on es6-promise and es6-collections
(and a handful of manual typings) rather than all of es6-shim.

Fixes angular#5242

We previously had an undocumented breaking change, this is now
documented in this commit.

Fixes angular#6817

BREAKING CHANGE:

Transitive typings are no longer included in the distribution.
You may need to install typings in your project using
http://github.com/typings/typings

Users now must rely on getting typings from:
- one of the peerDependencies, such as rxjs, which exposes
  typings via the moduleResolution=node mechanism.
  (see https://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages)
  This happens automatically.
- Using --target ES5 now requires manual installation of
  es6-promise and es6-collections typings.
- Using some angular APIs may introduce a dependency on eg. nodejs
  or jasmine, and those typings need manual installation as well.
  • Loading branch information
alexeagle committed Feb 4, 2016
1 parent decf115 commit 4509820
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 96 deletions.
7 changes: 4 additions & 3 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -981,15 +981,16 @@ gulp.task('!pre.test.typings.layoutNodeModule', ['build.js.cjs'], function() {
.pipe(gulp.dest(path.join(tmpdir, 'node_modules')));
});
gulp.task('!pre.test.typings.copyTypingsSpec', function() {
return gulp.src(['typing_spec/*.ts'], {base: 'typing_spec'}).pipe(gulp.dest(path.join(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'], function() {
var tsc = require('gulp-typescript');

return gulp.src([tmpdir + '/**'])
return gulp.src([tmpdir + '/*.ts'])
.pipe(tsc({
target: 'ES5',
target: 'ES6',
module: 'commonjs',
experimentalDecorators: true,
noImplicitAny: true,
Expand Down
36 changes: 0 additions & 36 deletions modules/angular2/manual_typings/globals-es6.d.ts

This file was deleted.

55 changes: 50 additions & 5 deletions modules/angular2/manual_typings/globals.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,52 @@
/**
* Declarations angular depends on for compilation to ES6.
* This file is also used to propagate our transitive typings
* to users.
* Subset of es6-shim typings.
* Angular should not require use of ES6 runtime but some API usages are already present.
* See https://github.com/angular/angular/issues/5242
* TODO(alexeagle): remove methods below which may not be present in targeted browser
*/
/// <reference path="../typings/es6-shim/es6-shim.d.ts"/>
/// <reference path="./globals-es6.d.ts"/>

declare type PromiseConstructor = typeof Promise;

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;

/**
* 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
* endPosition – length(this). Otherwise returns false.
*/
endsWith(searchString: string, endPosition?: number): boolean;
}
interface Array<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): T;
/**
* 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[];
}
interface NumberConstructor {
/**
* Returns true if the value passed is an integer, false otherwise.
* @param number A numeric value.
*/
isInteger(number: number): boolean;
}
8 changes: 0 additions & 8 deletions modules/angular2/src/core/change_detection/parser/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,12 @@ class _Scanner {
}

scanCharacter(start: number, code: number): Token {
assert(this.peek == code);
this.advance();
return newCharacterToken(start, code);
}


scanOperator(start: number, str: string): Token {
assert(this.peek == StringWrapper.charCodeAt(str, 0));
assert(SetWrapper.has(OPERATORS, str));
this.advance();
return newOperatorToken(start, str);
}
Expand All @@ -274,7 +271,6 @@ class _Scanner {
*/
scanComplexOperator(start: number, one: string, twoCode: number, two: string, threeCode?: number,
three?: string): Token {
assert(this.peek == StringWrapper.charCodeAt(one, 0));
this.advance();
var str: string = one;
if (this.peek == twoCode) {
Expand All @@ -285,12 +281,10 @@ class _Scanner {
this.advance();
str += three;
}
assert(SetWrapper.has(OPERATORS, str));
return newOperatorToken(start, str);
}

scanIdentifier(): Token {
assert(isIdentifierStart(this.peek));
var start: number = this.index;
this.advance();
while (isIdentifierPart(this.peek)) this.advance();
Expand All @@ -303,7 +297,6 @@ class _Scanner {
}

scanNumber(start: number): Token {
assert(isDigit(this.peek));
var simple: boolean = (this.index === start);
this.advance(); // Skip initial digit.
while (true) {
Expand All @@ -329,7 +322,6 @@ class _Scanner {
}

scanString(): Token {
assert(this.peek == $SQ || this.peek == $DQ);
var start: number = this.index;
var quote: number = this.peek;
this.advance(); // Skip initial quote.
Expand Down
9 changes: 5 additions & 4 deletions modules/angular2/src/core/zone/ng_zone.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {normalizeBlank, isPresent, global} from 'angular2/src/facade/lang';
import {normalizeBlank, isPresent, global, ZoneLike} from 'angular2/src/facade/lang';
import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
import {wtfLeave, wtfCreateScope, WtfScopeFn} from '../profile/profile';

export interface NgZoneZone extends Zone {
export interface NgZoneZone extends ZoneLike {
/** @internal */
_innerZone: boolean;
}
Expand Down Expand Up @@ -348,8 +348,9 @@ export class NgZone {
var errorHandling;

if (enableLongStackTrace) {
errorHandling = StringMapWrapper.merge(
Zone.longStackTraceZone, {onError: function(e) { ngZone._notifyOnError(this, e); }});
errorHandling =
StringMapWrapper.merge(global.Zone.longStackTraceZone,
{onError: function(e) { ngZone._notifyOnError(this, e); }});
} else {
errorHandling = {onError: function(e) { ngZone._notifyOnError(this, e); }};
}
Expand Down
14 changes: 5 additions & 9 deletions modules/angular2/src/facade/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,16 @@ import {toPromise} from 'rxjs/operator/toPromise';
export {Observable} from 'rxjs/Observable';
export {Subject} from 'rxjs/Subject';

export namespace NodeJS {
export interface Timer {}
}

export class TimerWrapper {
static setTimeout(fn: (...args: any[]) => void, millis: number): NodeJS.Timer {
static setTimeout(fn: (...args: any[]) => void, millis: number): number {
return global.setTimeout(fn, millis);
}
static clearTimeout(id: NodeJS.Timer): void { global.clearTimeout(id); }
static clearTimeout(id: number): void { global.clearTimeout(id); }

static setInterval(fn: (...args: any[]) => void, millis: number): NodeJS.Timer {
static setInterval(fn: (...args: any[]) => void, millis: number): number {
return global.setInterval(fn, millis);
}
static clearInterval(id: NodeJS.Timer): void { global.clearInterval(id); }
static clearInterval(id: number): void { global.clearInterval(id); }
}

export class ObservableWrapper {
Expand Down Expand Up @@ -161,4 +157,4 @@ export class EventEmitter<T> extends Subject<T> {

return super.subscribe(schedulerFn, errorFn, completeFn);
}
}
}
34 changes: 32 additions & 2 deletions modules/angular2/src/facade/lang.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
// Zones are TC-39 standards-track so users could choose a different implementation
// Rather than import {Zone} from 'zone.js' we define an interface
// so that any library that structurally matches may be used with Angular 2.
export interface ZoneLike {
fork(locals?: any): ZoneLike;
run(fn: any, applyTo?: any, applyWith?: any): any;
longStackTraceZone: { [key: string]: any; };
}

export interface BrowserNodeGlobal {
Object: typeof Object;
Array: typeof Array;
Map: typeof Map;
Set: typeof Set;
Date: DateConstructor;
RegExp: RegExpConstructor;
JSON: typeof JSON;
Math: any; // typeof Math;
assert(condition: any): void;
Reflect: any;
Zone: ZoneLike;
getAngularTestability: Function;
getAllAngularTestabilities: Function;
frameworkStabilizers: Array<Function>;
setTimeout: Function;
clearTimeout: Function;
setInterval: Function;
clearInterval: Function;
}

// TODO(jteplitz602): Load WorkerGlobalScope from lib.webworker.d.ts file #3492
declare var WorkerGlobalScope;
var globalScope: BrowserNodeGlobal;
Expand All @@ -10,7 +40,7 @@ if (typeof window === 'undefined') {
}
} else {
globalScope = <any>window;
};
}

export const IS_DART = false;

Expand Down Expand Up @@ -430,4 +460,4 @@ export function evalExpression(sourceUrl: string, expr: string, declarations: st

export function isPrimitive(obj: any): boolean {
return !isJsObject(obj);
}
}
16 changes: 14 additions & 2 deletions tools/broccoli/trees/browser_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ module.exports = function makeBrowserTree(options, destinationPath) {
patterns: [{match: /\$SCRIPTS\$/, replacement: jsReplace('SCRIPTS')}]
});

let ambientTypings = [
'angular2/typings/hammerjs/hammerjs.d.ts',
'angular2/typings/node/node.d.ts',
'angular2/manual_typings/globals.d.ts',
'angular2/typings/es6-collections/es6-collections.d.ts',
'angular2/typings/es6-promise/es6-promise.d.ts'
];

// Use TypeScript to transpile the *.ts files to ES5
var es5Tree = compileWithTypescript(es5ModulesTree, {
declaration: false,
Expand All @@ -180,7 +188,7 @@ module.exports = function makeBrowserTree(options, destinationPath) {
moduleResolution: 'classic',
noEmitOnError: !noTypeChecks,
rootDir: './',
rootFilePaths: ['angular2/manual_typings/globals.d.ts'],
rootFilePaths: ambientTypings,
inlineSourceMap: sourceMaps,
inlineSources: sourceMaps,
target: 'es5'
Expand Down Expand Up @@ -311,7 +319,11 @@ module.exports = function makeBrowserTree(options, destinationPath) {
experimentalDecorators: true,
noEmitOnError: false,
rootDir: './',
rootFilePaths: ['angular2/manual_typings/globals-es6.d.ts'],
rootFilePaths: [
'angular2/typings/zone.js/zone.js.d.ts',
'angular2/typings/hammerjs/hammerjs.d.ts',
'angular2/typings/node/node.d.ts',
],
inlineSourceMap: sourceMaps,
inlineSources: sourceMaps,
target: 'es6'
Expand Down
39 changes: 12 additions & 27 deletions tools/broccoli/trees/node_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ module.exports = function makeNodeTree(projects, destinationPath) {
]
});

let ambientTypings = [
'angular2/typings/hammerjs/hammerjs.d.ts',
'angular2/typings/node/node.d.ts',
'angular2/manual_typings/globals.d.ts',
'angular2/typings/es6-collections/es6-collections.d.ts',
'angular2/typings/es6-promise/es6-promise.d.ts'
];

// Compile the sources and generate the @internal .d.ts
let compiledSrcTreeWithInternals =
compileTree(srcTree, true, ['angular2/manual_typings/globals.d.ts']);
let compiledSrcTreeWithInternals = compileTree(srcTree, true, ambientTypings);

var testTree = new Funnel('modules', {
include: [
Expand Down Expand Up @@ -85,11 +92,10 @@ module.exports = function makeNodeTree(projects, destinationPath) {

testTree = mergeTrees([testTree, srcPrivateDeclarations]);

let compiledTestTree = compileTree(testTree, false, [
let compiledTestTree = compileTree(testTree, false, ambientTypings.concat([
'angular2/typings/jasmine/jasmine.d.ts',
'angular2/typings/angular-protractor/angular-protractor.d.ts',
'angular2/manual_typings/globals.d.ts'
]);
]));

// Merge the compiled sources and tests
let compiledSrcTree =
Expand All @@ -112,12 +118,7 @@ module.exports = function makeNodeTree(projects, destinationPath) {
var srcPkgJsons = extractPkgJsons(srcTree, BASE_PACKAGE_JSON);
var testPkgJsons = extractPkgJsons(testTree, BASE_PACKAGE_JSON);

var typingsTree = new Funnel(
'modules',
{include: ['angular2/typings/**/*.d.ts', 'angular2/manual_typings/*.d.ts'], destDir: '/'});

var nodeTree =
mergeTrees([compiledTree, srcDocs, testDocs, srcPkgJsons, testPkgJsons, typingsTree]);
var nodeTree = mergeTrees([compiledTree, srcDocs, testDocs, srcPkgJsons, testPkgJsons]);

// Transform all tests to make them runnable in node
nodeTree = replace(nodeTree, {
Expand All @@ -139,22 +140,6 @@ module.exports = function makeNodeTree(projects, destinationPath) {
nodeTree = replace(
nodeTree, {files: ['**/*.js'], patterns: [{match: /^/, replacement: () => `'use strict';`}]});

// Add a line to the end of our top-level .d.ts file.
// This HACK for transitive typings is a workaround for
// https://github.com/Microsoft/TypeScript/issues/5097
//
// This allows users to get our top-level dependencies like zone.d.ts
// to appear when they compile against angular2.
//
// This carries the risk that the user brings their own copy of that file
// (or any other symbols exported here) and they will get a compiler error
// because of the duplicate definitions.
// TODO(alexeagle): remove this when typescript releases a fix
nodeTree = replace(nodeTree, {
files: ['angular2/core.d.ts'],
patterns: [{match: /$/, replacement: 'import "./manual_typings/globals-es6.d.ts";\r\n'}]
});

return destCopy(nodeTree, destinationPath);
};

Expand Down

0 comments on commit 4509820

Please sign in to comment.