diff --git a/WORKSPACE b/WORKSPACE index c0425bbd59..cac374a782 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/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/assert.ts b/packages/core/src/render3/assert.ts index 0b1bbc7cea..c08e376924 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -52,6 +52,15 @@ export function assertNotNull(actual: T, msg: string) { } } +export function assertComponentType( + actual: any, + msg: string = + 'Type passed in is not ComponentType, it does not have \'ngComponentDef\' property.') { + if (!actual.ngComponentDef) { + throwError(msg); + } +} + 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 00550faa80..c8802d80c3 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); @@ -135,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/packages/core/test/bundling/hello_world/BUILD.bazel b/packages/core/test/bundling/hello_world/BUILD.bazel index 897b7f7f56..68c2a6f35a 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 8c015f9491..a92e44dd18 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 0000000000..c5c7bb3e0b --- /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 2f78d6e00b..47d9450b04 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 3c49929ea9..8dc964a517 100644 --- a/tools/symbol-extractor/symbol_extractor.ts +++ b/tools/symbol-extractor/symbol_extractor.ts @@ -22,15 +22,17 @@ 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); } + fnRecurseDepth--; break; case ts.SyntaxKind.SourceFile: case ts.SyntaxKind.VariableStatement: @@ -44,9 +46,13 @@ 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 && + isRollupExportSymbol(child.parent as ts.VariableDeclarationList)) { + ts.forEachChild(child, visitor); + } break; case ts.SyntaxKind.FunctionDeclaration: const funcDecl = child as ts.FunctionDeclaration; @@ -114,3 +120,15 @@ 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 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); +} \ 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 0000000000..2881163809 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.js @@ -0,0 +1,26 @@ +/** + * @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 + */ + +/** + * 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 + 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 0000000000..c09db23487 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/iife_with_export.json @@ -0,0 +1,3 @@ +[ + "A" +] \ No newline at end of file