From 20a900b648d952f074167a4788d113955968f4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mis=CC=8Cko=20Hevery?= Date: Fri, 2 Feb 2018 15:08:25 -0800 Subject: [PATCH] test: Add bundle symbol extractor tool (#22002) This tool will be used for extracting symbols out of bundles so that we can assert that only whitelisted symbols are allowed. PR Close #22002 --- .../test/bundling/hello_world/BUILD.bazel | 11 +- .../hello_world/bundle.golden_symbols.json | 83 +++ .../bundling/hello_world/treeshaking_spec.ts | 6 +- tools/symbol-extractor/BUILD.bazel | 40 ++ tools/symbol-extractor/cli.ts | 54 ++ tools/symbol-extractor/index.bzl | 22 + tools/symbol-extractor/symbol_extractor.ts | 116 +++ .../symbol-extractor/symbol_extractor_spec.ts | 38 + .../dont_pick_up_inner_symbols.js | 15 + .../dont_pick_up_inner_symbols.json | 4 + .../symbol_extractor_spec/empty.js | 9 + .../symbol_extractor_spec/empty.json | 1 + .../symbol_extractor_spec/empty_iife.js | 9 + .../symbol_extractor_spec/empty_iife.json | 1 + .../hello_world_min_debug.js | 675 ++++++++++++++++++ .../hello_world_min_debug.json | 28 + .../symbol_extractor_spec/simple.js | 9 + .../symbol_extractor_spec/simple.json | 8 + .../two_symbols_per_var.js | 23 + .../two_symbols_per_var.json | 8 + .../symbol_extractor_spec/var_list.js | 12 + .../symbol_extractor_spec/var_list.json | 5 + 22 files changed, 1172 insertions(+), 5 deletions(-) create mode 100644 packages/core/test/bundling/hello_world/bundle.golden_symbols.json create mode 100644 tools/symbol-extractor/BUILD.bazel create mode 100644 tools/symbol-extractor/cli.ts create mode 100644 tools/symbol-extractor/index.bzl create mode 100644 tools/symbol-extractor/symbol_extractor.ts create mode 100644 tools/symbol-extractor/symbol_extractor_spec.ts create mode 100644 tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.json create mode 100644 tools/symbol-extractor/symbol_extractor_spec/empty.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/empty.json create mode 100644 tools/symbol-extractor/symbol_extractor_spec/empty_iife.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/empty_iife.json create mode 100644 tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.json create mode 100644 tools/symbol-extractor/symbol_extractor_spec/simple.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/simple.json create mode 100644 tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.json create mode 100644 tools/symbol-extractor/symbol_extractor_spec/var_list.js create mode 100644 tools/symbol-extractor/symbol_extractor_spec/var_list.json diff --git a/packages/core/test/bundling/hello_world/BUILD.bazel b/packages/core/test/bundling/hello_world/BUILD.bazel index 3fddbe7d3b..5954284f18 100644 --- a/packages/core/test/bundling/hello_world/BUILD.bazel +++ b/packages/core/test/bundling/hello_world/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) load("//tools:defaults.bzl", "ts_library") +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") @@ -31,7 +32,9 @@ ts_library( name = "test_lib", testonly = 1, srcs = ["domino_typings.d.ts"] + glob(["*_spec.ts"]), - deps = ["//packages:types"], + deps = [ + "//packages:types", + ], ) jasmine_node_test( @@ -44,3 +47,9 @@ jasmine_node_test( ], deps = [":test_lib"], ) + +js_expected_symbol_test( + name = "symbol_test", + src = ":bundle.min_debug.js", + golden = ":bundle.golden_symbols.json", +) diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json new file mode 100644 index 0000000000..22e9147b83 --- /dev/null +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -0,0 +1,83 @@ +[ + { + "name": "EMPTY$1" + }, + { + "name": "NO_CHANGE" + }, + { + "name": "Symbol$1" + }, + { + "name": "__global$1" + }, + { + "name": "__self$1" + }, + { + "name": "__window$1" + }, + { + "name": "_renderCompCount" + }, + { + "name": "_root" + }, + { + "name": "canInsertNativeNode" + }, + { + "name": "createLNode" + }, + { + "name": "createLView" + }, + { + "name": "domRendererFactory3" + }, + { + "name": "enterView" + }, + { + "name": "executeHooks" + }, + { + "name": "findFirstRNode" + }, + { + "name": "getDirectiveInstance" + }, + { + "name": "getNextLNodeWithProjection" + }, + { + "name": "getNextOrParentSiblingNode" + }, + { + "name": "invertObject" + }, + { + "name": "isProceduralRenderer" + }, + { + "name": "leaveView" + }, + { + "name": "locateHostElement" + }, + { + "name": "noop$2" + }, + { + "name": "refreshDynamicChildren" + }, + { + "name": "renderComponentOrTemplate" + }, + { + "name": "renderEmbeddedTemplate" + }, + { + "name": "stringify$1" + } +] \ No newline at end of file diff --git a/packages/core/test/bundling/hello_world/treeshaking_spec.ts b/packages/core/test/bundling/hello_world/treeshaking_spec.ts index 2d46617eb5..69a6c7d256 100644 --- a/packages/core/test/bundling/hello_world/treeshaking_spec.ts +++ b/packages/core/test/bundling/hello_world/treeshaking_spec.ts @@ -18,10 +18,8 @@ import * as domino from 'domino'; describe('treeshaking with uglify', () => { let content: string; - beforeAll(() => { - content = fs.readFileSync( - path.join(process.env['TEST_SRCDIR'], PACKAGE, 'bundle.min_debug.js'), UTF8); - }); + const contentPath = require.resolve(path.join(PACKAGE, 'bundle.min_debug.js')); + beforeAll(() => { content = fs.readFileSync(contentPath, UTF8); }); it('should drop unused TypeScript helpers', () => { expect(content).not.toContain('__asyncGenerator'); }); diff --git a/tools/symbol-extractor/BUILD.bazel b/tools/symbol-extractor/BUILD.bazel new file mode 100644 index 0000000000..1a7d79470f --- /dev/null +++ b/tools/symbol-extractor/BUILD.bazel @@ -0,0 +1,40 @@ +package(default_visibility = ["//visibility:public"]) + +load("//tools:defaults.bzl", "ts_library") +load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test") + +ts_library( + name = "lib", + testonly = 1, + srcs = glob( + ["**/*.ts"], + exclude = [ + "**/*_spec.ts", + "**/*_spec", + ], + ), + deps = [ + "//packages:types", + ], +) + +ts_library( + name = "test_lib", + testonly = 1, + srcs = glob( + ["**/*_spec.ts"], + exclude = ["symbol_extractor_spec/**"], + ), + deps = [ + ":lib", + "//packages:types", + ], +) + +jasmine_node_test( + name = "test", + data = glob(["symbol_extractor_spec/**"]), + deps = [ + ":test_lib", + ], +) diff --git a/tools/symbol-extractor/cli.ts b/tools/symbol-extractor/cli.ts new file mode 100644 index 0000000000..915b732257 --- /dev/null +++ b/tools/symbol-extractor/cli.ts @@ -0,0 +1,54 @@ +/** + * @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 + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import {SymbolExtractor} from './symbol_extractor'; + +// These keys are arbitrary and local to this test. +const update_var = 'UPDATE_GOLDEN'; +const update_val = 1; + +if (require.main === module) { + const doUpdate = process.env[update_var] == update_val; + const args = process.argv.slice(2) as[string, string]; + process.exitCode = main(args, doUpdate) ? 0 : 1; +} + +/** + * CLI main method. + * + * ``` + * cli javascriptFilePath.js goldenFilePath.json + * ``` + */ +function main(argv: [string, string], doUpdate: boolean): boolean { + const javascriptFilePath = require.resolve(argv[0]); + const goldenFilePath = require.resolve(argv[1]); + + const javascriptContent = fs.readFileSync(javascriptFilePath).toString(); + const goldenContent = fs.readFileSync(goldenFilePath).toString(); + + const symbolExtractor = new SymbolExtractor(javascriptFilePath, javascriptContent); + + let passed: boolean = false; + if (doUpdate) { + fs.writeFileSync(goldenFilePath, JSON.stringify(symbolExtractor.actual, undefined, 2)); + console.error('Updated gold file:', goldenFilePath); + passed = true; + } else { + passed = symbolExtractor.compareAndPrintError(goldenFilePath, goldenContent); + if (!passed) { + console.error(`TEST FAILED!`); + console.error(` To update the golden file run: `); + console.error( + ` bazel run --define ${update_var}=${update_val} ${process.env['BAZEL_TARGET']}`); + } + } + return passed; +} diff --git a/tools/symbol-extractor/index.bzl b/tools/symbol-extractor/index.bzl new file mode 100644 index 0000000000..e1af4475f2 --- /dev/null +++ b/tools/symbol-extractor/index.bzl @@ -0,0 +1,22 @@ +# 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 + +"""This test verifies that a set of top level symbols from a javascript file match a gold file. +""" + +load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_test") + +def js_expected_symbol_test(name, src, golden, **kwargs): + all_data = [src, golden] + all_data += [Label("//tools/symbol-extractor:lib")] + entry_point = "angular/tools/symbol-extractor/cli.js" + + nodejs_test( + name = name, + data = all_data, + entry_point = entry_point, + templated_args = ["$(location %s)" % src, "$(location %s)" % golden], + **kwargs + ) diff --git a/tools/symbol-extractor/symbol_extractor.ts b/tools/symbol-extractor/symbol_extractor.ts new file mode 100644 index 0000000000..3c49929ea9 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor.ts @@ -0,0 +1,116 @@ +/** + * @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 + */ + +import * as fs from 'fs'; +import * as ts from 'typescript'; + + +export interface Symbol { name: string; } + +export class SymbolExtractor { + public actual: Symbol[]; + + static symbolSort(a: Symbol, b: Symbol): number { + return a.name == b.name ? 0 : a.name < b.name ? -1 : 1; + } + + 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; + function visitor(child: ts.Node) { + switch (child.kind) { + case ts.SyntaxKind.FunctionExpression: + fnDepth++; + if (fnDepth <= 1) { + // Only go into function expression once for the outer closure. + ts.forEachChild(child, visitor); + } + break; + case ts.SyntaxKind.SourceFile: + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.VariableDeclarationList: + case ts.SyntaxKind.ExpressionStatement: + case ts.SyntaxKind.CallExpression: + case ts.SyntaxKind.ParenthesizedExpression: + case ts.SyntaxKind.Block: + case ts.SyntaxKind.PrefixUnaryExpression: + ts.forEachChild(child, visitor); + break; + case ts.SyntaxKind.VariableDeclaration: + const varDecl = child as ts.VariableDeclaration; + if (varDecl.initializer) { + symbols.push({name: varDecl.name.getText()}); + } + break; + case ts.SyntaxKind.FunctionDeclaration: + const funcDecl = child as ts.FunctionDeclaration; + funcDecl.name && symbols.push({name: funcDecl.name.getText()}); + break; + default: + // Left for easier debugging. + // console.log('###', ts.SyntaxKind[child.kind], child.getText()); + } + if (symbols.length && symbols[symbols.length - 1].name == 'type') { + debugger; + } + } + visitor(source); + symbols.sort(SymbolExtractor.symbolSort); + return symbols; + } + + static diff(actual: Symbol[], expected: string|((Symbol | string)[])): {[name: string]: string} { + if (typeof expected == 'string') { + expected = JSON.parse(expected); + } + const diff: {[name: string]: ('missing' | 'extra')} = {}; + (expected as(Symbol | string)[]).forEach((nameOrSymbol) => { + diff[typeof nameOrSymbol == 'string' ? nameOrSymbol : nameOrSymbol.name] = 'missing'; + }); + + actual.forEach((s) => { + if (diff[s.name] === 'missing') { + delete diff[s.name]; + } else { + diff[s.name] = 'extra'; + } + }); + return diff; + } + + constructor(private path: string, private contents: string) { + this.actual = SymbolExtractor.parse(path, contents); + } + + expect(expectedSymbols: (string|Symbol)[]) { + expect(SymbolExtractor.diff(this.actual, expectedSymbols)).toEqual({}); + } + + compareAndPrintError(goldenFilePath: string, expected: string|((Symbol | string)[])): boolean { + let passed = true; + const diff = SymbolExtractor.diff(this.actual, expected); + Object.keys(diff).forEach((key) => { + if (passed) { + console.error(`Expected symbols in '${this.path}' did not match gold file.`); + passed = false; + } + console.error(` Symbol: ${key} => ${diff[key]}`); + }); + + return passed; + } +} + +function toSymbol(v: string | Symbol): Symbol { + return typeof v == 'string' ? {'name': v} : v as Symbol; +} + +function toName(symbol: Symbol): string { + return symbol.name; +} diff --git a/tools/symbol-extractor/symbol_extractor_spec.ts b/tools/symbol-extractor/symbol_extractor_spec.ts new file mode 100644 index 0000000000..5a5dcbfb2b --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec.ts @@ -0,0 +1,38 @@ +/** + * @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 + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as ts from 'typescript'; + +import {Symbol, SymbolExtractor} from './symbol_extractor'; + +describe('scenarios', () => { + const symbolExtractorSpecDir = path.dirname( + require.resolve('angular/tools/symbol-extractor/symbol_extractor_spec/empty.json')); + const scenarioFiles = fs.readdirSync(symbolExtractorSpecDir); + for (let i = 0; i < scenarioFiles.length; i = i + 2) { + let jsFile = scenarioFiles[i]; + let jsonFile = scenarioFiles[i + 1]; + let testName = jsFile.substring(0, jsFile.lastIndexOf('.')); + if (!jsFile.endsWith('.js')) throw new Error('Expected: .js file found: ' + jsFile); + if (!jsonFile.endsWith('.json')) throw new Error('Expected: .json file found: ' + jsonFile); + + // Left here so that it is easy to debug single test. + // if (testName !== 'hello_world_min_debug') continue; + + it(testName, () => { + const jsFileContent = fs.readFileSync(path.join(symbolExtractorSpecDir, jsFile)).toString(); + const jsonFileContent = + fs.readFileSync(path.join(symbolExtractorSpecDir, jsonFile)).toString(); + const symbols = SymbolExtractor.parse(testName, jsFileContent); + const diff = SymbolExtractor.diff(symbols, jsonFileContent); + expect(diff).toEqual({}); + }); + } +}); diff --git a/tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.js b/tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.js new file mode 100644 index 0000000000..29a6ab8998 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.js @@ -0,0 +1,15 @@ +/** + * @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 + */ + +!function() { + function A() { + function ignoreA() {} + } + function B() { let ignoreB = {}; } + !function() { let ignoreC = {}; }; +}(); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.json b/tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.json new file mode 100644 index 0000000000..e09a8db68f --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/dont_pick_up_inner_symbols.json @@ -0,0 +1,4 @@ +[ + "A", + "B" +] \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/empty.js b/tools/symbol-extractor/symbol_extractor_spec/empty.js new file mode 100644 index 0000000000..fa2078a2c1 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/empty.js @@ -0,0 +1,9 @@ +/** + * @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 + */ + +export let __empty__; diff --git a/tools/symbol-extractor/symbol_extractor_spec/empty.json b/tools/symbol-extractor/symbol_extractor_spec/empty.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/empty_iife.js b/tools/symbol-extractor/symbol_extractor_spec/empty_iife.js new file mode 100644 index 0000000000..3b96c85080 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/empty_iife.js @@ -0,0 +1,9 @@ +/** + * @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 + */ + +(function() {})(); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/empty_iife.json b/tools/symbol-extractor/symbol_extractor_spec/empty_iife.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/empty_iife.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.js b/tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.js new file mode 100644 index 0000000000..5eea71c7e5 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.js @@ -0,0 +1,675 @@ +/** + * @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 + */ + +!function() { + 'use strict'; + /** +*@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 +*/ + /** +*@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 ChangeDetectionStrategy, ChangeDetectorStatus, ViewEncapsulation; + Object; + ChangeDetectionStrategy || (ChangeDetectionStrategy = {}); + ChangeDetectorStatus || (ChangeDetectorStatus = {}); + /** + * @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 + */ Object; + ViewEncapsulation || (ViewEncapsulation = {}); + /** +*@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 +*/ + /** + * @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 + */ 'undefined' != typeof window && + window, + 'undefined' != typeof self && 'undefined' != typeof WorkerGlobalScope && + self instanceof WorkerGlobalScope && self, + 'undefined' != typeof global && global; + /** +*@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 +*/ + String; + /** +*@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 +*/ + Function; + var __window$1 = 'undefined' != typeof window && window, __self$1 = 'undefined' != typeof self && + 'undefined' != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope && self, + __global$1 = 'undefined' != typeof global && global, + _root = __window$1 || __global$1 || __self$1; + !function() { + if (!_root) throw new Error('RxJS could not find any global context (window, self, global)'); + }(); + Array; + !function() { Object.setPrototypeOf || Array; }(); + Error; + var RendererStyleFlags2, Symbol$1 = _root.Symbol; + 'function' == typeof Symbol$1 && 'function' == typeof Symbol$1.for && + Symbol$1.for ('rxSubscriber'), + function() { Object.setPrototypeOf || Array; }(); + !function(context) { + var $$observable, Symbol = _root.Symbol; + if ('function' == typeof Symbol) + if (Symbol.observable) + $$observable = Symbol.observable; + else { + $$observable = Symbol('observable'); + Symbol.observable = $$observable; + } + else + $$observable = '@@observable'; + }(); + (function() { Object.setPrototypeOf || Array; })(), + function() { Object.setPrototypeOf || Array; }(); + !function() { Object.setPrototypeOf || Array; }(); + (function(root) { + var Symbol = root.Symbol; + if ('function' == typeof Symbol) { + Symbol.iterator || (Symbol.iterator = Symbol('iterator polyfill')); + return Symbol.iterator; + } + var Set_1 = root.Set; + if (Set_1 && 'function' == typeof new Set_1()['@@iterator']) return '@@iterator'; + var Map_1 = root.Map; + if (Map_1) + for (var keys = Object.getOwnPropertyNames(Map_1.prototype), i = 0; i < keys.length; ++i) { + var key = keys[i]; + if ('entries' !== key && 'size' !== key && Map_1.prototype[key] === Map_1.prototype.entries) + return key; + } + })(_root), + function() { Object.setPrototypeOf || Array; }(); + (function() { Object.setPrototypeOf || Array; })(), + function() { Object.setPrototypeOf || Array; }(); + !function() { Object.setPrototypeOf || Array; }(); + Error, function() { Object.setPrototypeOf || Array; }(), function() { + Object.setPrototypeOf || Array; + }(), function() { Object.setPrototypeOf || Array; }(); + !function() { Object.setPrototypeOf || Array; }(); + Object; + RendererStyleFlags2 || (RendererStyleFlags2 = {}); + var RendererStyleFlags3, _renderCompCount = 0; + function executeHooks(data, allHooks, checkHooks, creationMode) { + var hooksToCall = creationMode ? allHooks : checkHooks; + null != hooksToCall && + function(data, arr) { + for (var i = 0; i < arr.length; i += 2) arr[1 | i].call(data[arr[i]]); + } + /** + * @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 + */ + /** + * @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 + */ + /** + * @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 + */ + /** + * @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 + */ (data, hooksToCall); + } + RendererStyleFlags3 || (RendererStyleFlags3 = {}); + var domRendererFactory3 = { + createRenderer: function(hostElement, rendererType) { return document; } + }; + /** +*@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 +*/ + /** + * @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 + */ function + getNextLNodeWithProjection(node) { + var pNextOrParent = node.pNextOrParent; + return pNextOrParent ? 1 == (3 & pNextOrParent.flags) ? null : pNextOrParent : node.next; + } + function getNextOrParentSiblingNode(initialNode, rootNode) { + for (var node = initialNode, nextNode = getNextLNodeWithProjection(node); node && !nextNode;) { + if ((node = node.pNextOrParent || node.parent) === rootNode) return null; + nextNode = node && getNextLNodeWithProjection(node); + } + return nextNode; + } + function findFirstRNode(rootNode) { + for (var node = rootNode; node;) { + var type = 3 & node.flags, nextNode = null; + if (3 === type) return node.native; + if (0 === type) { + var childContainerData = node.data; + nextNode = childContainerData.views.length ? childContainerData.views[0].child : null; + } else + nextNode = 1 === type ? node.data.head : node.child; + node = null === nextNode ? getNextOrParentSiblingNode(node, rootNode) : nextNode; + } + return null; + } + function canInsertNativeNode(parent, view) { + return 3 == (3 & parent.flags) && (parent.view !== view || null === parent.data); + } + function stringify$1(value) { + return 'function' == typeof value ? + value.name || value : + 'string' == typeof value ? value : null == value ? '' : '' + value; + } + /** +*@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 renderer, rendererFactory, previousOrParentNode, isParent, tData, currentView, currentQueries, + creationMode, data, bindingIndex; + currentView = createLView(null, null, createTView()); + function enterView(newView, host) { + var oldView = currentView; + data = newView.data; + bindingIndex = newView.bindingStartIndex || 0; + tData = newView.tView.data; + creationMode = newView.creationMode; + renderer = newView.renderer; + if (null != host) { + previousOrParentNode = host; + isParent = !0; + } + currentView = newView; + currentQueries = newView.queries; + return oldView; + } + function leaveView(newView) { + executeHooks( + currentView.data, currentView.tView.viewHooks, currentView.tView.viewCheckHooks, + creationMode); + currentView.creationMode = !1; + currentView.lifecycleStage = 1; + currentView.tView.firstTemplatePass = !1; + enterView(newView, null); + } + function createLView(viewId, renderer, tView, template, context) { + void 0 === template && (template = null); + void 0 === context && (context = null); + return { + parent: currentView, + id: viewId, + node: null, + data: [], + tView: tView, + cleanup: null, + renderer: renderer, + child: null, + tail: null, + next: null, + bindingStartIndex: null, + creationMode: !0, + template: template, + context: context, + dynamicViewCount: 0, + lifecycleStage: 1, + queries: null + }; + } + function createLNode(index, type, native, state) { + var parent = + isParent ? previousOrParentNode : previousOrParentNode && previousOrParentNode.parent, + queries = + (isParent ? currentQueries : previousOrParentNode && previousOrParentNode.queries) || + parent && parent.queries && parent.queries.child(), + isState = null != state, node = { + flags: type, + native: native, + view: currentView, + parent: parent, + child: null, + next: null, + nodeInjector: parent ? parent.nodeInjector : null, + data: isState ? state : null, + queries: queries, + tNode: null, + pNextOrParent: null + }; + 2 == (2 & type) && isState && (state.node = node); + if (null != index) { + data[index] = node; + index >= tData.length ? tData[index] = null : node.tNode = tData[index]; + if (isParent) { + currentQueries = null; + previousOrParentNode.view !== currentView && 2 != (3 & previousOrParentNode.flags) || + (previousOrParentNode.child = node); + } else + previousOrParentNode && (previousOrParentNode.next = node); + } + previousOrParentNode = node; + isParent = !0; + return node; + } + function renderEmbeddedTemplate(viewNode, template, context, renderer) { + var _isParent = isParent, _previousOrParentNode = previousOrParentNode; + try { + isParent = !0; + previousOrParentNode = null; + var cm = !1; + if (null == viewNode) { + viewNode = + createLNode(null, 2, null, createLView(-1, renderer, createTView(), template, context)); + cm = !0; + } + enterView(viewNode.data, viewNode); + template(context, cm); + } finally { + refreshDynamicChildren(); + leaveView(currentView.parent); + isParent = _isParent; + previousOrParentNode = _previousOrParentNode; + } + return viewNode; + } + function renderComponentOrTemplate(node, hostView, componentOrContext, template) { + var oldView = enterView(hostView, node); + try { + rendererFactory.begin && rendererFactory.begin(); + template ? template(componentOrContext, creationMode) : function( + directiveIndex, elementIndex) { + !function(currentView, tView, creationMode) { + if (1 === currentView.lifecycleStage) { + executeHooks(currentView.data, tView.initHooks, tView.checkHooks, creationMode); + currentView.lifecycleStage = 2; + } + }(currentView, currentView.tView, creationMode); + !function(currentView, tView, creationMode) { + if (currentView.lifecycleStage < 3) { + executeHooks( + currentView.data, tView.contentHooks, tView.contentCheckHooks, creationMode); + currentView.lifecycleStage = 3; + } + }(currentView, currentView.tView, creationMode); + var template = tData[1].template; + if (null != template) { + var element = data[0], directive = getDirectiveInstance(data[1]), + oldView = enterView(element.data, element); + try { + template(directive, creationMode); + } finally { + refreshDynamicChildren(); + leaveView(oldView); + } + } + }(); + } finally { + rendererFactory.end && rendererFactory.end(); + leaveView(oldView); + } + } + function createTView() { + return { + data: [], + firstTemplatePass: !0, + initHooks: null, + checkHooks: null, + contentHooks: null, + contentCheckHooks: null, + viewHooks: null, + viewCheckHooks: null, + destroyHooks: null, + objectLiterals: null + }; + } + function locateHostElement(factory, elementOrSelector) { + rendererFactory = factory; + var defaultRenderer = factory.createRenderer(null, null); + return 'string' == typeof elementOrSelector ? + defaultRenderer.selectRootElement ? defaultRenderer.selectRootElement(elementOrSelector) : + defaultRenderer.querySelector(elementOrSelector) : + elementOrSelector; + } + function refreshDynamicChildren() { + for (var current = currentView.child; null !== current; current = current.next) + if (0 !== current.dynamicViewCount && current.views) + for (var container_1 = current, i = 0; i < container_1.views.length; i++) { + var view = container_1.views[i]; + renderEmbeddedTemplate(view, view.data.template, view.data.context, renderer); + } + } + var NO_CHANGE = {}; + function getDirectiveInstance(instanceOrArray) { + return Array.isArray(instanceOrArray) ? instanceOrArray[0] : instanceOrArray; + } + var EMPTY$1 = {}; + function noop$2() {} + function invertObject(obj) { + if (null == obj) return EMPTY$1; + var newObj = {}; + for (var minifiedKey in obj) newObj[obj[minifiedKey]] = minifiedKey; + return newObj; + } + /** +*@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 +*/ + /** +*@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 +*/ + /** +*@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 +*/ + /** +*@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 +*/ + /** + * @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 + */ Object; + /** + * @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 + */ ! + /** + * @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 + */ + function(componentType, opts) { + void 0 === opts && (opts = {}); + var component, rendererFactory = opts.rendererFactory || domRendererFactory3, + componentDef = componentType.ngComponentDef; + componentDef.type != componentType && (componentDef.type = componentType); + var hostNode = locateHostElement(rendererFactory, opts.host || componentDef.tag), + oldView = enterView( + createLView( + -1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), + createTView()), + null); + try { + !function(rNode, def) { + !function() { + isParent = !1; + previousOrParentNode = null; + }(); + createLNode(0, 3, rNode, createLView(-1, renderer, function(template) { + return template.ngPrivateData || (template.ngPrivateData = createTView()); + }(def.template))); + }(hostNode, componentDef); + component = getDirectiveInstance(function(index, directive, directiveDef, queryName) { + var instance, flags = previousOrParentNode.flags; + 0 == (4092 & flags) ? flags = 4100 | 3 & flags : flags += 4; + previousOrParentNode.flags = flags; + Object.defineProperty( + directive, '__ngHostLNode__', {enumerable: !1, value: previousOrParentNode}); + data[1] = instance = directive; + if (1 >= tData.length) { + tData[1] = directiveDef; + } + var diPublic = directiveDef.diPublic; + diPublic && diPublic(directiveDef); + var tNode = previousOrParentNode.tNode; + tNode && tNode.attrs && function(instance, inputs, tNode) { + var directiveIndex = ((4092 & previousOrParentNode.flags) >> 2) - 1, + initialInputData = tNode.initialInputs; + (void 0 === initialInputData || directiveIndex >= initialInputData.length) && + (initialInputData = function(directiveIndex, inputs, tNode) { + var initialInputData = tNode.initialInputs || (tNode.initialInputs = []); + initialInputData[directiveIndex] = null; + for (var attrs = tNode.attrs, i = 0; i < attrs.length; i += 2) { + var minifiedInputName = inputs[attrs[i]]; + void 0 !== minifiedInputName && + (initialInputData[directiveIndex] || (initialInputData[directiveIndex] = [ + ])).push(minifiedInputName, attrs[1 | i]); + } + return initialInputData; + }(directiveIndex, directiveDef.inputs, tNode)); + var initialInputs = initialInputData[directiveIndex]; + if (initialInputs) + for (var i = 0; i < initialInputs.length; i += 2) + instance[initialInputs[i]] = initialInputs[1 | i]; + }(instance, 0, tNode); + ! + /** + * @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 + */ + function(index, onInit, doCheck, tView) { + if (!0 === tView.firstTemplatePass) { + null != onInit && (tView.initHooks || (tView.initHooks = [])).push(1, onInit); + if (null != doCheck) { + (tView.initHooks || (tView.initHooks = [])).push(1, doCheck); + (tView.checkHooks || (tView.checkHooks = [])).push(1, doCheck); + } + } + }(0, directiveDef.onInit, directiveDef.doCheck, currentView.tView); + return instance; + }(0, componentDef.n(), componentDef)); + } finally { + leaveView(oldView); + } + opts.features && + opts.features.forEach(function(feature) { return feature(component, componentDef); }); + !function(component) { + var hostNode = component.__ngHostLNode__; + renderComponentOrTemplate(hostNode, hostNode.view, component); + } + /** + * @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 + */ + /** + * @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 + */ (component); + }(function() { + function HelloWorld() { this.name = 'World'; } + HelloWorld.ngComponentDef = function(componentDefinition) { + var type = componentDefinition.type, def = { + type: type, + diPublic: null, + n: componentDefinition.factory, + tag: componentDefinition.tag || null, + template: componentDefinition.template || null, + h: componentDefinition.hostBindings || noop$2, + inputs: invertObject(componentDefinition.inputs), + outputs: invertObject(componentDefinition.outputs), + methods: invertObject(componentDefinition.methods), + rendererType: function(type) { + if (type && '$$undefined' === type.id) { + var isFilled = + null != type.encapsulation && type.encapsulation !== ViewEncapsulation.None || + type.styles.length || Object.keys(type.data).length; + type.id = isFilled ? 'c' + _renderCompCount++ : '$$empty'; + } + type && '$$empty' === type.id && (type = null); + return type || null; + }(componentDefinition.rendererType) || + null, + exportAs: componentDefinition.exportAs, + onInit: type.prototype.ngOnInit || null, + doCheck: type.prototype.ngDoCheck || null, + afterContentInit: type.prototype.ngAfterContentInit || null, + afterContentChecked: type.prototype.ngAfterContentChecked || null, + afterViewInit: type.prototype.ngAfterViewInit || null, + afterViewChecked: type.prototype.ngAfterViewChecked || null, + onDestroy: type.prototype.ngOnDestroy || null + }, + feature = componentDefinition.features; + feature && feature.forEach(function(fn) { return fn(def); }); + return def; + }({ + type: HelloWorld, + tag: 'hello-world', + factory: function() { return new HelloWorld(); }, + template: function(ctx, cm) { + cm && function(index, value) { + createLNode(0, 3, null); + isParent = !1; + }(); + !function(index, value) { + var existingNode = data[0]; + if (existingNode.native) + value !== NO_CHANGE && + (renderer.setValue ? + renderer.setValue(existingNode.native, stringify$1(value)) : + existingNode.native.textContent = stringify$1(value)); + else { + existingNode.native = renderer.createText ? + renderer.createText(stringify$1(value)) : + renderer.createTextNode(stringify$1(value)); + !function(node, currentView) { + var parent = node.parent; + if (canInsertNativeNode(parent, currentView)) { + var nativeSibling = function(node, stopNode) { + for (var currentNode = node; currentNode && null !== currentNode;) { + var pNextOrParent = currentNode.pNextOrParent; + if (pNextOrParent) { + for (var pNextOrParentType = 3 & pNextOrParent.flags; + 1 !== pNextOrParentType;) { + if (nativeNode = findFirstRNode(pNextOrParent)) return nativeNode; + pNextOrParent = pNextOrParent.pNextOrParent; + } + currentNode = pNextOrParent; + } else { + for (var currentSibling = currentNode.next; currentSibling;) { + var nativeNode; + if (nativeNode = findFirstRNode(currentSibling)) return nativeNode; + currentSibling = currentSibling.next; + } + var parentNode = currentNode.parent; + currentNode = null; + if (parentNode) { + var parentType = 3 & parentNode.flags; + 0 !== parentType && 2 !== parentType || (currentNode = parentNode); + } + } + } + return null; + }(node), renderer = currentView.renderer; + renderer.listen ? + renderer.insertBefore(parent.native, node.native, nativeSibling) : + parent.native.insertBefore(node.native, nativeSibling, !1); + } + } + /** +* @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 +*/ + /** + * @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 + */ (existingNode, currentView); + } + }(0, function(prefix, value, suffix) { + return function(value) { + if (creationMode) { + !function() { + null == currentView.bindingStartIndex && + (bindingIndex = currentView.bindingStartIndex = data.length); + }(); + return data[bindingIndex++] = value; + } + var changed = value !== NO_CHANGE && function(a, b) { + return !(a != a && value != value) && a !== value; + }(data[bindingIndex]); + changed && (data[bindingIndex] = value); + bindingIndex++; + return changed ? value : NO_CHANGE; + }(value) === NO_CHANGE ? + NO_CHANGE : + 'Hello ' + stringify$1(value) + '!'; + }(0, ctx.name)); + } + }); + return HelloWorld; + }()); +}(); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.json b/tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.json new file mode 100644 index 0000000000..aab644f771 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/hello_world_min_debug.json @@ -0,0 +1,28 @@ +[ "EMPTY$1", + "NO_CHANGE", + "Symbol$1", + "__global$1", + "__self$1", + "__window$1", + "_renderCompCount", + "_root", + "createLNode", + "createLView", + "domRendererFactory3", + "enterView", + "invertObject", + "leaveView", + "locateHostElement", + "noop$2", + "refreshDynamicChildren", + "renderComponentOrTemplate", + "renderEmbeddedTemplate", + "stringify$1", + "canInsertNativeNode", + "createTView", + "executeHooks", + "findFirstRNode", + "getDirectiveInstance", + "getNextLNodeWithProjection", + "getNextOrParentSiblingNode" +] \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/simple.js b/tools/symbol-extractor/symbol_extractor_spec/simple.js new file mode 100644 index 0000000000..02afdb0417 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/simple.js @@ -0,0 +1,9 @@ +/** + * @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 + */ + +(function() { var Class = function() {}, fn = function() {}; })(); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/simple.json b/tools/symbol-extractor/symbol_extractor_spec/simple.json new file mode 100644 index 0000000000..40de35bc5f --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/simple.json @@ -0,0 +1,8 @@ +[ + { + "name": "Class" + }, + { + "name": "fn" + } +] \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.js b/tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.js new file mode 100644 index 0000000000..3326d87b10 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.js @@ -0,0 +1,23 @@ +/** + * @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 + */ + +!function() { + 'use strict'; + // tslint:disable-next-line:no-console + console.log('Hello, Alice in Wonderland'); + var A = function() { + function A() {} + return A.prototype.a = function() { return document.a; }, A; + }(), B = function() { + function B() {} + return B.prototype.b = function() { return window.b; }, B; + }(); + var ignore_no_initializer; + // tslint:disable-next-line:no-console + console.error(new A().a(), new B().b()); +}(); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.json b/tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.json new file mode 100644 index 0000000000..62227abf69 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/two_symbols_per_var.json @@ -0,0 +1,8 @@ +[ + { + "name": "A" + }, + { + "name": "B" + } +] \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/var_list.js b/tools/symbol-extractor/symbol_extractor_spec/var_list.js new file mode 100644 index 0000000000..a9bd6f8887 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/var_list.js @@ -0,0 +1,12 @@ +/** + * @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 + */ + +!function() { + 'use strict'; + var constant = 1, method = function() {}, clazz = class {}; +}(); \ No newline at end of file diff --git a/tools/symbol-extractor/symbol_extractor_spec/var_list.json b/tools/symbol-extractor/symbol_extractor_spec/var_list.json new file mode 100644 index 0000000000..1f180d75c6 --- /dev/null +++ b/tools/symbol-extractor/symbol_extractor_spec/var_list.json @@ -0,0 +1,5 @@ +[ + "clazz", + "constant", + "method" +] \ No newline at end of file