From 5bcf29785bda95eb40d71c62570d0d308499f79d Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Wed, 14 Mar 2018 21:32:09 -0700 Subject: [PATCH 1/5] test(ivy): switch HelloWorld to ivy compiler --- WORKSPACE | 6 ++-- packages/core/src/render3/assert.ts | 7 +++++ packages/core/src/render3/component.ts | 11 +++++-- .../test/bundling/hello_world/BUILD.bazel | 15 +++++++-- .../hello_world/bundle.golden_symbols.json | 9 ++++++ .../core/test/bundling/hello_world/index.html | 31 +++++++++++++++++++ .../core/test/bundling/hello_world/index.ts | 21 ++++++------- tools/symbol-extractor/symbol_extractor.ts | 29 ++++++++++++++--- .../symbol_extractor_spec/iife_with_export.js | 21 +++++++++++++ .../iife_with_export.json | 3 ++ 10 files changed, 128 insertions(+), 25 deletions(-) create mode 100644 packages/core/test/bundling/hello_world/index.html create mode 100644 tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/iife_with_export.json diff --git a/WORKSPACE b/WORKSPACE index c0425bbd59266..71fc62c1ce518 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,9 +2,9 @@ workspace(name = "angular") http_archive( name = "build_bazel_rules_nodejs", - url = "https://github.com/bazelbuild/rules_nodejs/archive/0.5.3.zip", - strip_prefix = "rules_nodejs-0.5.3", - sha256 = "17a5515f59777b00cb25dbc710017a14273f825029b2ec60e0969d28914870be", + url = "https://github.com/mhevery/rules_nodejs/archive/07c2cf1f04fa7b68220d730bc8df37ee9811b5e8.zip", + strip_prefix = "rules_nodejs-07c2cf1f04fa7b68220d730bc8df37ee9811b5e8", + sha256 = "7dbdec2d73a83fd0a34dbe3b459b810fd0b3e580152bc3fcd339784ad4dae988", ) load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories") diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index 0b1bbc7cea9ed..e590df791000b 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -52,6 +52,13 @@ export function assertNotNull(actual: T, msg: string) { } } +export function assertComponentType(actual: any) { + if (!actual.ngComponentDef) { + throwError( + 'Type passed in is not ComponentType, it does not have \'ngComponentDef\' property.'); + } +} + function throwError(msg: string): never { throw new Error(`ASSERTION ERROR: ${msg}`); } \ No newline at end of file diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 00550faa80ee3..68eaceb8dfc47 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -8,10 +8,11 @@ // We are temporarily importing the existing viewEngine from core so we can be sure we are // correctly implementing its interfaces for backwards compatibility. +import {Type} from '../core'; import {Injector} from '../di/injector'; import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; -import {assertNotNull} from './assert'; +import {assertComponentType, assertNotNull} from './assert'; import {queueInitHooks, queueLifecycleHooks} from './hooks'; import {CLEAN_PROMISE, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, enterView, getRootView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderComponentOrTemplate} from './instructions'; import {ComponentDef, ComponentType} from './interfaces/definition'; @@ -113,9 +114,13 @@ export const NULL_INJECTOR: Injector = { * @param options Optional parameters which control bootstrapping */ export function renderComponent( - componentType: ComponentType, opts: CreateComponentOptions = {}): T { + componentType: ComponentType| + Type/* Type as workaround for: Microsoft/TypeScript/issues/4881 */ + , + opts: CreateComponentOptions = {}): T { + ngDevMode && assertComponentType(componentType); const rendererFactory = opts.rendererFactory || domRendererFactory3; - const componentDef = componentType.ngComponentDef as ComponentDef; + const componentDef = (componentType as ComponentType).ngComponentDef as ComponentDef; if (componentDef.type != componentType) componentDef.type = componentType; let component: T; const hostNode = locateHostElement(rendererFactory, opts.host || componentDef.tag); diff --git a/packages/core/test/bundling/hello_world/BUILD.bazel b/packages/core/test/bundling/hello_world/BUILD.bazel index 897b7f7f56f08..68c2a6f35ae66 100644 --- a/packages/core/test/bundling/hello_world/BUILD.bazel +++ b/packages/core/test/bundling/hello_world/BUILD.bazel @@ -1,11 +1,12 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "ts_library") +load("//tools:defaults.bzl", "ts_library", "ivy_ng_module") load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test") load("//packages/bazel/src:ng_rollup_bundle.bzl", "ng_rollup_bundle") load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test") +load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver") -ts_library( +ivy_ng_module( name = "hello_world", srcs = ["index.ts"], deps = [ @@ -54,3 +55,13 @@ js_expected_symbol_test( src = ":bundle.min_debug.js", golden = ":bundle.golden_symbols.json", ) + +ts_devserver( + name = "devserver", + static_files = [ + ":bundle.min_debug.js", + ":bundle.min.js", + "index.html", + ], + deps = [], +) diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 8c015f9491a14..a92e44dd1893a 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -8,6 +8,12 @@ { "name": "EMPTY_RENDERER_TYPE_ID" }, + { + "name": "HelloWorld" + }, + { + "name": "INeedToExistEvenThoughtIAmNotNeeded" + }, { "name": "NG_HOST_SYMBOL" }, @@ -68,6 +74,9 @@ { "name": "defineComponent" }, + { + "name": "defineInjector" + }, { "name": "detectChangesInternal" }, diff --git a/packages/core/test/bundling/hello_world/index.html b/packages/core/test/bundling/hello_world/index.html new file mode 100644 index 0000000000000..c5c7bb3e0b56a --- /dev/null +++ b/packages/core/test/bundling/hello_world/index.html @@ -0,0 +1,31 @@ + + + + + Angular Hello World Example + + + + + + + + + \ No newline at end of file diff --git a/packages/core/test/bundling/hello_world/index.ts b/packages/core/test/bundling/hello_world/index.ts index 2f78d6e00b41f..47d9450b04f32 100644 --- a/packages/core/test/bundling/hello_world/index.ts +++ b/packages/core/test/bundling/hello_world/index.ts @@ -6,19 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵT as T, ɵdefineComponent as defineComponent, ɵrenderComponent as renderComponent} from '@angular/core'; +import {Component, NgModule, ɵrenderComponent as renderComponent} from '@angular/core'; -class HelloWorld { - static ngComponentDef = defineComponent({ - type: HelloWorld, - tag: 'hello-world', - factory: () => new HelloWorld(), - template: function HelloWorldTemplate(ctx: HelloWorld, cm: boolean) { - if (cm) { - T(0, 'Hello World!'); - } - } - }); +@Component({selector: 'hello-world', template: 'Hello World!'}) +export class HelloWorld { } +// TODO(misko): Forgetting to export HelloWorld and not having NgModule fails silently. + +@NgModule({declarations: [HelloWorld]}) +export class INeedToExistEvenThoughtIAmNotNeeded { +} +// TODO(misko): Package should not be required to make this work. renderComponent(HelloWorld); diff --git a/tools/symbol-extractor/symbol_extractor.ts b/tools/symbol-extractor/symbol_extractor.ts index 3c49929ea920f..bb2650ddca0a4 100644 --- a/tools/symbol-extractor/symbol_extractor.ts +++ b/tools/symbol-extractor/symbol_extractor.ts @@ -22,13 +22,14 @@ export class SymbolExtractor { static parse(path: string, contents: string): Symbol[] { const symbols: Symbol[] = []; const source: ts.SourceFile = ts.createSourceFile(path, contents, ts.ScriptTarget.Latest, true); - let fnDepth = 0; + let fnRecurseDepth = 0; function visitor(child: ts.Node) { + // Left for easier debugging. + // console.log('>>>', ts.SyntaxKind[child.kind]); switch (child.kind) { case ts.SyntaxKind.FunctionExpression: - fnDepth++; - if (fnDepth <= 1) { - // Only go into function expression once for the outer closure. + fnRecurseDepth++; + if (fnRecurseDepth <= 1) { ts.forEachChild(child, visitor); } break; @@ -44,9 +45,12 @@ export class SymbolExtractor { break; case ts.SyntaxKind.VariableDeclaration: const varDecl = child as ts.VariableDeclaration; - if (varDecl.initializer) { + if (varDecl.initializer && fnRecurseDepth !== 0) { symbols.push({name: varDecl.name.getText()}); } + if (fnRecurseDepth == 0 && isStoringIIFE(child.parent as ts.VariableDeclarationList)) { + ts.forEachChild(child, visitor); + } break; case ts.SyntaxKind.FunctionDeclaration: const funcDecl = child as ts.FunctionDeclaration; @@ -114,3 +118,18 @@ function toSymbol(v: string | Symbol): Symbol { function toName(symbol: Symbol): string { return symbol.name; } + +/** + * Detects if VariableDeclarationList is format `var x = function(){}()`; + * + * Rollup produces this format when it wants to export symbols from a bundle. + * @param child + */ +function isStoringIIFE(child: ts.VariableDeclarationList): boolean { + if (child.declarations.length !== 1) return false; + const decl: ts.VariableDeclaration = child.declarations[0]; + if (decl.initializer && decl.initializer.kind == ts.SyntaxKind.CallExpression) { + return true; + } + return false; +} \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js new file mode 100644 index 0000000000000..b8a4f3102aca5 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var fooBar = function(exports) { + 'use strict'; + // tslint:disable-next-line:no-console + console.log('Hello, Alice in Wonderland'); + var A = function() { + function A() {} + A.prototype.a = function() { return document.a; }; + return A; + }(); + // tslint:disable-next-line:no-console + console.error(new A().a()); + exports.A = A; + return exports; +}({}); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.json b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.json new file mode 100644 index 0000000000000..c09db23487d85 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.json @@ -0,0 +1,3 @@ +[ + "A" +] \ No newline at end of file From cbcc6b8b5d9b5bdb3bb58779a0c38dee62fd8e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Mon, 19 Mar 2018 14:09:58 -0700 Subject: [PATCH 2/5] fixup! test(ivy): switch HelloWorld to ivy compiler --- WORKSPACE | 6 +++--- packages/core/src/render3/component.ts | 2 +- tools/symbol-extractor/symbol_extractor.ts | 10 ++++------ .../symbol_extractor_spec/iife_with_export.js | 5 +++++ 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 71fc62c1ce518..cac374a782bb4 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,9 +2,9 @@ workspace(name = "angular") http_archive( name = "build_bazel_rules_nodejs", - url = "https://github.com/mhevery/rules_nodejs/archive/07c2cf1f04fa7b68220d730bc8df37ee9811b5e8.zip", - strip_prefix = "rules_nodejs-07c2cf1f04fa7b68220d730bc8df37ee9811b5e8", - sha256 = "7dbdec2d73a83fd0a34dbe3b459b810fd0b3e580152bc3fcd339784ad4dae988", + url = "https://github.com/bazelbuild/rules_nodejs/archive/25bb70fb67bddcc257b869f434ccc0fd130ec3bd.zip", + strip_prefix = "rules_nodejs-25bb70fb67bddcc257b869f434ccc0fd130ec3bd", + sha256 = "11c0d73bdcb4b2608abbe5967be5a910bdaebf848eb13e4e7f8413bbdeb940b8", ) load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories") diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 68eaceb8dfc47..c8802d80c3570 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -140,7 +140,7 @@ export function renderComponent( try { // Create element node at index 0 in data array elementNode = hostElement(hostNode, componentDef); - // Create directive instance with n() and store at index 1 in data array (el is 0) + // Create directive instance with factory() and store at index 1 in data array (el is 0) component = rootContext.component = baseDirectiveCreate(1, componentDef.factory(), componentDef) as T; initChangeDetectorIfExisting(elementNode.nodeInjector, component); diff --git a/tools/symbol-extractor/symbol_extractor.ts b/tools/symbol-extractor/symbol_extractor.ts index bb2650ddca0a4..1797269732ada 100644 --- a/tools/symbol-extractor/symbol_extractor.ts +++ b/tools/symbol-extractor/symbol_extractor.ts @@ -32,6 +32,7 @@ export class SymbolExtractor { if (fnRecurseDepth <= 1) { ts.forEachChild(child, visitor); } + fnRecurseDepth--; break; case ts.SyntaxKind.SourceFile: case ts.SyntaxKind.VariableStatement: @@ -48,7 +49,7 @@ export class SymbolExtractor { if (varDecl.initializer && fnRecurseDepth !== 0) { symbols.push({name: varDecl.name.getText()}); } - if (fnRecurseDepth == 0 && isStoringIIFE(child.parent as ts.VariableDeclarationList)) { + if (fnRecurseDepth == 0 && isRollupExportSymbol(child.parent as ts.VariableDeclarationList)) { ts.forEachChild(child, visitor); } break; @@ -125,11 +126,8 @@ function toName(symbol: Symbol): string { * Rollup produces this format when it wants to export symbols from a bundle. * @param child */ -function isStoringIIFE(child: ts.VariableDeclarationList): boolean { +function isRollupExportSymbol(child: ts.VariableDeclarationList): boolean { if (child.declarations.length !== 1) return false; const decl: ts.VariableDeclaration = child.declarations[0]; - if (decl.initializer && decl.initializer.kind == ts.SyntaxKind.CallExpression) { - return true; - } - return false; + return (decl.initializer && decl.initializer.kind == ts.SyntaxKind.CallExpression); } \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js index b8a4f3102aca5..2881163809edc 100644 --- a/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js +++ b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js @@ -5,6 +5,11 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ + +/** + * Rollup exports symbols in this particular way. This test demonstrates that we can correctly read + * symbols. + */ var fooBar = function(exports) { 'use strict'; // tslint:disable-next-line:no-console From a049f4d2233f22c8513a3985b1b0a155b90452e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Mon, 19 Mar 2018 14:09:58 -0700 Subject: [PATCH 3/5] fixup! test(ivy): switch HelloWorld to ivy compiler --- tools/symbol-extractor/symbol_extractor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/symbol-extractor/symbol_extractor.ts b/tools/symbol-extractor/symbol_extractor.ts index 1797269732ada..6e9a0450b659c 100644 --- a/tools/symbol-extractor/symbol_extractor.ts +++ b/tools/symbol-extractor/symbol_extractor.ts @@ -129,5 +129,5 @@ function toName(symbol: Symbol): string { function isRollupExportSymbol(child: ts.VariableDeclarationList): boolean { if (child.declarations.length !== 1) return false; const decl: ts.VariableDeclaration = child.declarations[0]; - return (decl.initializer && decl.initializer.kind == ts.SyntaxKind.CallExpression); + return !!(decl.initializer && decl.initializer.kind == ts.SyntaxKind.CallExpression); } \ No newline at end of file From 5b1371fa925ee5c6ab2fb6963493ef48ad843c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Mon, 19 Mar 2018 14:09:58 -0700 Subject: [PATCH 4/5] fixup! test(ivy): switch HelloWorld to ivy compiler --- packages/core/src/render3/assert.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index e590df791000b..c08e376924ac0 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -52,10 +52,12 @@ export function assertNotNull(actual: T, msg: string) { } } -export function assertComponentType(actual: any) { +export function assertComponentType( + actual: any, + msg: string = + 'Type passed in is not ComponentType, it does not have \'ngComponentDef\' property.') { if (!actual.ngComponentDef) { - throwError( - 'Type passed in is not ComponentType, it does not have \'ngComponentDef\' property.'); + throwError(msg); } } From 9d1a8edb57f8ef017f33a9a496d7c4d1eba2836c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Mon, 19 Mar 2018 14:09:58 -0700 Subject: [PATCH 5/5] fixup! test(ivy): switch HelloWorld to ivy compiler --- tools/symbol-extractor/symbol_extractor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/symbol-extractor/symbol_extractor.ts b/tools/symbol-extractor/symbol_extractor.ts index 6e9a0450b659c..8dc964a517b69 100644 --- a/tools/symbol-extractor/symbol_extractor.ts +++ b/tools/symbol-extractor/symbol_extractor.ts @@ -49,7 +49,8 @@ export class SymbolExtractor { if (varDecl.initializer && fnRecurseDepth !== 0) { symbols.push({name: varDecl.name.getText()}); } - if (fnRecurseDepth == 0 && isRollupExportSymbol(child.parent as ts.VariableDeclarationList)) { + if (fnRecurseDepth == 0 && + isRollupExportSymbol(child.parent as ts.VariableDeclarationList)) { ts.forEachChild(child, visitor); } break;