From 4e585bca287d92ffd4220492f2028ede848dd880 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Mon, 16 Nov 2015 16:18:59 -0800 Subject: [PATCH] feat(build): add an option to disable type checks when running tests Since editors and IDEs do typechecking and show errors in place, often there is no benefit to running type checking in our test pipeline. This PR allows you to disable type checking: gulp test.unit.js --noTypeChecks This commit also makes es6 generation optional. fix(build): removes unnecessary circular dependencies Closes #5299 --- gulpfile.js | 27 ++++++---- .../directives/checkbox_value_accessor.ts | 5 +- .../directives/default_value_accessor.ts | 5 +- .../forms/directives/number_value_accessor.ts | 5 +- .../select_control_value_accessor.ts | 3 +- .../src/common/forms/directives/shared.ts | 7 --- .../src/core/linker/element_injector.ts | 20 +++---- tools/broccoli/angular_builder.ts | 52 ++++++++++++++----- tools/broccoli/trees/browser_tree.ts | 51 ++++++++++-------- 9 files changed, 103 insertions(+), 72 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 03b25d1a58..99c708c083 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -58,13 +58,14 @@ if (cliArgs.projects) { // --projects=angular2,angular2_material => {angular2: true, angular2_material: true} var allProjects = - 'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground'; + 'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground,bundle_deps'; var cliArgsProjects = (cliArgs.projects || allProjects) .split(',') .reduce((map, projectName) => { map[projectName] = true; return map; }, {}); +var generateEs6 = !cliArgs.projects; function printModulesWarning() { if (!cliArgs.projects && !process.env.CI) { @@ -302,11 +303,11 @@ gulp.task('lint', ['build.tools'], function() { // ------------ // check circular dependencies in Node.js context gulp.task('build/checkCircularDependencies', function(done) { - var dependencyObject = madge(CONFIG.dest.js.dev.es6, { - format: 'es6', - paths: [CONFIG.dest.js.dev.es6], + var dependencyObject = madge(CONFIG.dest.js.dev.es5, { + format: 'cjs', + paths: [CONFIG.dest.js.dev.es5], extensions: ['.js'], - onParseFile: function(data) { data.src = data.src.replace(/import \* as/g, "//import * as"); } + onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); } }); var circularDependencies = dependencyObject.circular().getArray(); if (circularDependencies.length > 0) { @@ -841,11 +842,15 @@ gulp.task('!build.tools', function() { gulp.task('broccoli.js.dev', ['build.tools'], function(done) { runSequence('!broccoli.js.dev', sequenceComplete(done)); }); -gulp.task('!broccoli.js.dev', - () => { return angularBuilder.rebuildBrowserDevTree(cliArgsProjects); }); +gulp.task( + '!broccoli.js.dev', + () => angularBuilder.rebuildBrowserDevTree( + {generateEs6: generateEs6, projects: cliArgsProjects, noTypeChecks: cliArgs.noTypeChecks})); -gulp.task('!broccoli.js.prod', - function() { return angularBuilder.rebuildBrowserProdTree(cliArgsProjects); }); +gulp.task( + '!broccoli.js.prod', + () => angularBuilder.rebuildBrowserProdTree( + {generateEs6: generateEs6, projects: cliArgsProjects, noTypeChecks: cliArgs.noTypeChecks})); gulp.task('build.js.dev', ['build/clean.js'], function(done) { runSequence('broccoli.js.dev', 'build.css.material', sequenceComplete(done)); @@ -868,7 +873,9 @@ var firstBuildJsCjs = true; * private task */ gulp.task('!build.js.cjs', function() { - return angularBuilder.rebuildNodeTree(cliArgsProjects) + return angularBuilder + .rebuildNodeTree( + {generateEs6: generateEs6, projects: cliArgsProjects, noTypeChecks: cliArgs.noTypeChecks}) .then(function() { if (firstBuildJsCjs) { firstBuildJsCjs = false; diff --git a/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts b/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts index 2e886a5dbb..2958d70b84 100644 --- a/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts +++ b/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts @@ -5,7 +5,6 @@ import {Self, forwardRef, Provider} from 'angular2/src/core/di'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor'; import {CONST_EXPR} from 'angular2/src/facade/lang'; -import {setProperty} from './shared'; const CHECKBOX_VALUE_ACCESSOR = CONST_EXPR(new Provider( NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => CheckboxControlValueAccessor), multi: true})); @@ -30,7 +29,9 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} - writeValue(value: any): void { setProperty(this._renderer, this._elementRef, "checked", value); } + writeValue(value: any): void { + this._renderer.setElementProperty(this._elementRef, 'checked', value); + } registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; } registerOnTouched(fn: () => {}): void { this.onTouched = fn; } } diff --git a/modules/angular2/src/common/forms/directives/default_value_accessor.ts b/modules/angular2/src/common/forms/directives/default_value_accessor.ts index 836ee088b3..970270bbca 100644 --- a/modules/angular2/src/common/forms/directives/default_value_accessor.ts +++ b/modules/angular2/src/common/forms/directives/default_value_accessor.ts @@ -4,7 +4,6 @@ import {Renderer} from 'angular2/src/core/render'; import {Self, forwardRef, Provider} from 'angular2/src/core/di'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor'; import {isBlank, CONST_EXPR} from 'angular2/src/facade/lang'; -import {setProperty} from './shared'; const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider( NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DefaultValueAccessor), multi: true})); @@ -39,9 +38,9 @@ export class DefaultValueAccessor implements ControlValueAccessor { writeValue(value: any): void { var normalizedValue = isBlank(value) ? '' : value; - setProperty(this._renderer, this._elementRef, 'value', normalizedValue); + this._renderer.setElementProperty(this._elementRef, 'value', normalizedValue); } registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } registerOnTouched(fn: () => void): void { this.onTouched = fn; } -} +} \ No newline at end of file diff --git a/modules/angular2/src/common/forms/directives/number_value_accessor.ts b/modules/angular2/src/common/forms/directives/number_value_accessor.ts index 82c5caac46..d3dd233ff3 100644 --- a/modules/angular2/src/common/forms/directives/number_value_accessor.ts +++ b/modules/angular2/src/common/forms/directives/number_value_accessor.ts @@ -4,7 +4,6 @@ import {Renderer} from 'angular2/src/core/render'; import {Self, forwardRef, Provider} from 'angular2/src/core/di'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor'; import {isBlank, CONST_EXPR, NumberWrapper} from 'angular2/src/facade/lang'; -import {setProperty} from './shared'; const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider( NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => NumberValueAccessor), multi: true})); @@ -34,7 +33,9 @@ export class NumberValueAccessor implements ControlValueAccessor { constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} - writeValue(value: number): void { setProperty(this._renderer, this._elementRef, 'value', value); } + writeValue(value: number): void { + this._renderer.setElementProperty(this._elementRef, 'value', value); + } registerOnChange(fn: (_: number) => void): void { this.onChange = (value) => { fn(NumberWrapper.parseFloat(value)); }; diff --git a/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts b/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts index b27e66dc4c..2fe478a309 100644 --- a/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts +++ b/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts @@ -6,7 +6,6 @@ import {Query, Directive} from 'angular2/src/core/metadata'; import {ObservableWrapper} from 'angular2/src/facade/async'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor'; import {CONST_EXPR} from 'angular2/src/facade/lang'; -import {setProperty} from './shared'; const SELECT_VALUE_ACCESSOR = CONST_EXPR(new Provider( NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => SelectControlValueAccessor), multi: true})); @@ -50,7 +49,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor { writeValue(value: any): void { this.value = value; - setProperty(this._renderer, this._elementRef, "value", value); + this._renderer.setElementProperty(this._elementRef, 'value', value); } registerOnChange(fn: () => any): void { this.onChange = fn; } diff --git a/modules/angular2/src/common/forms/directives/shared.ts b/modules/angular2/src/common/forms/directives/shared.ts index e0f753dd88..a0a5bb53ec 100644 --- a/modules/angular2/src/common/forms/directives/shared.ts +++ b/modules/angular2/src/common/forms/directives/shared.ts @@ -9,8 +9,6 @@ import {NgControlGroup} from './ng_control_group'; import {Control, ControlGroup} from '../model'; import {Validators} from '../validators'; import {ControlValueAccessor} from './control_value_accessor'; -import {ElementRef, QueryList} from 'angular2/src/core/linker'; -import {Renderer} from 'angular2/src/core/render'; import {DefaultValueAccessor} from './default_value_accessor'; import {NumberValueAccessor} from './number_value_accessor'; import {CheckboxControlValueAccessor} from './checkbox_value_accessor'; @@ -57,11 +55,6 @@ function _throwError(dir: AbstractControlDirective, message: string): void { throw new BaseException(`${message} '${path}'`); } -export function setProperty(renderer: Renderer, elementRef: ElementRef, propName: string, - propValue: any) { - renderer.setElementProperty(elementRef, propName, propValue); -} - export function composeValidators(validators: /* Array */ any[]): Function { return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null; } diff --git a/modules/angular2/src/core/linker/element_injector.ts b/modules/angular2/src/core/linker/element_injector.ts index b684eef96c..13c210c49b 100644 --- a/modules/angular2/src/core/linker/element_injector.ts +++ b/modules/angular2/src/core/linker/element_injector.ts @@ -34,8 +34,8 @@ import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/ import {AttributeMetadata, QueryMetadata} from '../metadata/di'; -import * as viewModule from './view'; -import * as avmModule from './view_manager'; +import {AppViewContainer, AppView} from './view'; +/* circular */ import * as avmModule from './view_manager'; import {ViewContainerRef} from './view_container_ref'; import {ElementRef} from './element_ref'; import {TemplateRef} from './template_ref'; @@ -183,8 +183,8 @@ export class DirectiveProvider extends ResolvedProvider_ { // TODO(rado): benchmark and consider rolling in as ElementInjector fields. export class PreBuiltObjects { - nestedView: viewModule.AppView = null; - constructor(public viewManager: avmModule.AppViewManager, public view: viewModule.AppView, + nestedView: AppView = null; + constructor(public viewManager: avmModule.AppViewManager, public view: AppView, public elementRef: ElementRef, public templateRef: TemplateRef) {} } @@ -195,7 +195,7 @@ export class QueryMetadataWithSetter { export class EventEmitterAccessor { constructor(public eventName: string, public getter: Function) {} - subscribe(view: viewModule.AppView, boundElementIndex: number, directive: Object): Object { + subscribe(view: AppView, boundElementIndex: number, directive: Object): Object { var eventEmitter = this.getter(directive); return ObservableWrapper.subscribe( eventEmitter, @@ -235,7 +235,7 @@ function _createProtoQueryRefs(providers: ProviderWithVisibility[]): ProtoQueryR } export class ProtoElementInjector { - view: viewModule.AppView; + view: AppView; attributes: Map; eventEmitterAccessors: EventEmitterAccessor[][]; protoQueryRefs: ProtoQueryRef[]; @@ -451,9 +451,9 @@ export class ElementInjector extends TreeNode implements Depend return new ViewContainerRef_(this._preBuiltObjects.viewManager, this.getElementRef()); } - getNestedView(): viewModule.AppView { return this._preBuiltObjects.nestedView; } + getNestedView(): AppView { return this._preBuiltObjects.nestedView; } - getView(): viewModule.AppView { return this._preBuiltObjects.view; } + getView(): AppView { return this._preBuiltObjects.view; } directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; } @@ -1044,13 +1044,13 @@ export class QueryRef { } } - private _visitViewContainer(vc: viewModule.AppViewContainer, aggregator: any[]) { + private _visitViewContainer(vc: AppViewContainer, aggregator: any[]) { for (var j = 0; j < vc.views.length; j++) { this._visitView(vc.views[j], aggregator); } } - private _visitView(view: viewModule.AppView, aggregator: any[]) { + private _visitView(view: AppView, aggregator: any[]) { for (var i = view.elementOffset; i < view.elementOffset + view.ownBindersCount; i++) { var inj = view.elementInjectors[i]; if (isBlank(inj)) continue; diff --git a/tools/broccoli/angular_builder.ts b/tools/broccoli/angular_builder.ts index 699c3d6a02..0f35cc50d5 100644 --- a/tools/broccoli/angular_builder.ts +++ b/tools/broccoli/angular_builder.ts @@ -11,6 +11,13 @@ type ProjectMap = { [key: string]: boolean }; +type Options = { + projects: ProjectMap; +noTypeChecks: boolean; +generateEs6: boolean; +} +; + /** * BroccoliBuilder facade for all of our build pipelines. */ @@ -25,20 +32,20 @@ export class AngularBuilder { constructor(public options: AngularBuilderOptions) { this.outputPath = options.outputPath; } - public rebuildBrowserDevTree(projects: ProjectMap): Promise { - this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(projects); + public rebuildBrowserDevTree(opts: Options): Promise { + this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(opts); return this.rebuild(this.browserDevBuilder, 'js.dev'); } - public rebuildBrowserProdTree(projects: ProjectMap): Promise { - this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(projects); + public rebuildBrowserProdTree(opts: Options): Promise { + this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(opts); return this.rebuild(this.browserProdBuilder, 'js.prod'); } - public rebuildNodeTree(projects: ProjectMap): Promise { - this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(projects); + public rebuildNodeTree(opts: Options): Promise { + this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(opts.projects); return this.rebuild(this.nodeBuilder, 'js.cjs'); } @@ -58,16 +65,30 @@ export class AngularBuilder { } - private makeBrowserDevBuilder(projects: ProjectMap): BroccoliBuilder { - let tree = makeBrowserTree({name: 'dev', typeAssertions: true, projects: projects}, - path.join(this.outputPath, 'js', 'dev')); + private makeBrowserDevBuilder(opts: Options): BroccoliBuilder { + let tree = makeBrowserTree( + { + name: 'dev', + typeAssertions: true, + projects: opts.projects, + noTypeChecks: opts.noTypeChecks, + generateEs6: opts.generateEs6 + }, + path.join(this.outputPath, 'js', 'dev')); return new broccoli.Builder(tree); } - private makeBrowserProdBuilder(projects: ProjectMap): BroccoliBuilder { - let tree = makeBrowserTree({name: 'prod', typeAssertions: false, projects: projects}, - path.join(this.outputPath, 'js', 'prod')); + private makeBrowserProdBuilder(opts: Options): BroccoliBuilder { + let tree = makeBrowserTree( + { + name: 'prod', + typeAssertions: false, + projects: opts.projects, + noTypeChecks: opts.noTypeChecks, + generateEs6: opts.generateEs6 + }, + path.join(this.outputPath, 'js', 'prod')); return new broccoli.Builder(tree); } @@ -128,11 +149,14 @@ function broccoliNodeToBuildNode(broccoliNode) { return new BuildNode(tree.description || tree.constructor.name, tree.inputPath ? [tree.inputPath] : tree.inputPaths, tree.cachePath, - tree.outputPath, broccoliNode.subtrees.map(broccoliNodeToBuildNode)); + tree.outputPath, broccoliNode.selfTime / (1000 * 1000 * 1000), + broccoliNode.totalTime / (1000 * 1000 * 1000), + broccoliNode.subtrees.map(broccoliNodeToBuildNode)); } class BuildNode { constructor(public pluginName: string, public inputPaths: string[], public cachePath: string, - public outputPath: string, public inputNodes: BroccoliNode[]) {} + public outputPath: string, public selfTime: number, public totalTime: number, + public inputNodes: BroccoliNode[]) {} } diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index 2685d0fd1e..c72665954b 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -70,7 +70,9 @@ const kServedPaths = [ module.exports = function makeBrowserTree(options, destinationPath) { - var modules = options.projects; + const modules = options.projects; + const noTypeChecks = options.noTypeChecks; + const generateEs6 = options.generateEs6; if (modules.angular2) { var angular2Tree = new Funnel('modules/angular2', { @@ -110,9 +112,6 @@ module.exports = function makeBrowserTree(options, destinationPath) { var modulesTree = mergeTrees( [angular2Tree, angular2MaterialTree, benchmarksTree, benchmarksExternalTree, playgroundTree]); - var clientModules = new Funnel( - 'node_modules', {include: ['@reactivex/**/**', 'parse5/**/**', 'css/**/**'], destDir: '/'}); - var es6PolyfillTypings = new Funnel('modules', {include: ['angular2/typings/es6-*/**'], destDir: '/'}); @@ -139,20 +138,6 @@ module.exports = function makeBrowserTree(options, destinationPath) { patterns: [{match: /\$SCRIPTS\$/, replacement: jsReplace('SCRIPTS')}] }); - // Use TypeScript to transpile the *.ts files to ES6 - var es6Tree = compileWithTypescript(modulesTree, { - declaration: false, - emitDecoratorMetadata: true, - experimentalDecorators: true, - mapRoot: '', // force sourcemaps to use relative path - noEmitOnError: false, - rootDir: './', - rootFilePaths: ['angular2/manual_typings/globals-es6.d.ts'], - sourceMap: true, - sourceRoot: '.', - target: 'es6' - }); - // Use TypeScript to transpile the *.ts files to ES5 var es5Tree = compileWithTypescript(es5ModulesTree, { declaration: false, @@ -161,7 +146,7 @@ module.exports = function makeBrowserTree(options, destinationPath) { mapRoot: '', // force sourcemaps to use relative path module: 'commonjs', moduleResolution: 'classic', - noEmitOnError: true, + noEmitOnError: !noTypeChecks, rootDir: './', rootFilePaths: ['angular2/manual_typings/globals.d.ts'], sourceMap: true, @@ -274,10 +259,32 @@ module.exports = function makeBrowserTree(options, destinationPath) { htmlTree = mergeTrees([htmlTree, scripts, polymer, react]); } - es5Tree = mergeTrees([es5Tree, htmlTree, assetsTree, clientModules]); - es6Tree = mergeTrees([es6Tree, htmlTree, assetsTree, clientModules]); + // this is needed only for creating a bundle + // typescript resolves dependencies automatically + if (modules.bundle_deps) { + var nodeModules = new Funnel( + 'node_modules', {include: ['@reactivex/**/**', 'parse5/**/**', 'css/**/**'], destDir: '/'}); + } - var mergedTree = mergeTrees([stew.mv(es6Tree, '/es6'), stew.mv(es5Tree, '/es5')]); + if (generateEs6) { + // Use TypeScript to transpile the *.ts files to ES6 + var es6Tree = compileWithTypescript(modulesTree, { + declaration: false, + emitDecoratorMetadata: true, + experimentalDecorators: true, + mapRoot: '', // force sourcemaps to use relative path + noEmitOnError: false, + rootDir: './', + rootFilePaths: ['angular2/manual_typings/globals-es6.d.ts'], + sourceMap: true, + sourceRoot: '.', + target: 'es6' + }); + es6Tree = stew.mv(mergeTrees([es6Tree, htmlTree, assetsTree, nodeModules]), '/es6'); + } + es5Tree = stew.mv(mergeTrees([es5Tree, htmlTree, assetsTree, nodeModules]), '/es5'); + + var mergedTree = mergeTrees([es6Tree, es5Tree]); return destCopy(mergedTree, destinationPath); };