From a37bcc3bfe2f11b30b0899b30024ff92521b6c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Tue, 31 Jul 2018 11:14:06 -0700 Subject: [PATCH] feat(ivy): bridge component styles into the component renderer (#25255) PR Close #25255 --- .../src/ngtsc/annotations/src/component.ts | 15 +- .../src/ngtsc/annotations/src/directive.ts | 25 +- .../r3_view_compiler_styling_spec.ts | 85 +- packages/compiler/src/compile_metadata.ts | 2 + packages/compiler/src/render3/view/api.ts | 17 + .../compiler/src/render3/view/compiler.ts | 21 + packages/compiler/src/style_compiler.ts | 4 +- packages/core/src/render3/definition.ts | 13 +- .../core/src/render3/interfaces/definition.ts | 4 +- packages/core/src/render3/jit/directive.ts | 3 + .../hello_world/bundle.golden_symbols.json | 5 +- .../hello_world_r2/bundle.golden_symbols.json | 4 +- .../injection/bundle.golden_symbols.json | 2 +- .../bundling/todo/bundle.golden_symbols.json | 5 +- packages/core/test/bundling/todo/index.ts | 3 +- .../core/test/bundling/todo_r2/BUILD.bazel | 116 + packages/core/test/bundling/todo_r2/base.css | 141 + .../todo_r2/bundle.golden_symbols.json | 2402 +++++++++++++++++ .../core/test/bundling/todo_r2/index.html | 57 + packages/core/test/bundling/todo_r2/index.ts | 165 ++ packages/core/test/bundling/todo_r2/todo.css | 372 +++ .../test/bundling/todo_r2/todo_e2e_spec.ts | 51 + packages/core/test/render3/component_spec.ts | 8 + .../core/test/render3/integration_spec.ts | 37 + .../test/render3/renderer_factory_spec.ts | 4 + .../test/render3/view_container_ref_spec.ts | 20 + 26 files changed, 3557 insertions(+), 24 deletions(-) create mode 100644 packages/core/test/bundling/todo_r2/BUILD.bazel create mode 100644 packages/core/test/bundling/todo_r2/base.css create mode 100644 packages/core/test/bundling/todo_r2/bundle.golden_symbols.json create mode 100644 packages/core/test/bundling/todo_r2/index.html create mode 100644 packages/core/test/bundling/todo_r2/index.ts create mode 100644 packages/core/test/bundling/todo_r2/todo.css create mode 100644 packages/core/test/bundling/todo_r2/todo_e2e_spec.ts diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 7f8805d46f..f71deaf16a 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -15,7 +15,7 @@ import {filterToMembersWithDecorator, reflectObjectLiteral, staticallyResolve} f import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform'; import {ResourceLoader} from './api'; -import {extractDirectiveMetadata, extractQueriesFromDecorator, queriesFromFields} from './directive'; +import {extractDirectiveMetadata, extractQueriesFromDecorator, parseFieldArrayValue, queriesFromFields} from './directive'; import {SelectorScopeRegistry} from './selector_scope'; import {isAngularCore, unwrapExpression} from './util'; @@ -135,11 +135,24 @@ export class ComponentDecoratorHandler implements DecoratorHandler, field: string, reflector: ReflectionHost, + checker: ts.TypeChecker): null|string[] { + if (!directive.has(field)) { + return null; + } + + // Resolve the field of interest from the directive metadata to a string[]. + const value = staticallyResolve(directive.get(field) !, reflector, checker); + if (!isStringArrayOrDie(value, field)) { + throw new Error(`Failed to resolve @Directive.${field}`); + } + + return value; +} + /** * Interpret property mapping fields on the decorator (e.g. inputs or outputs) and return the * correctly shaped metadata object. @@ -268,16 +284,11 @@ function isStringArrayOrDie(value: any, name: string): value is string[] { function parseFieldToPropertyMapping( directive: Map, field: string, reflector: ReflectionHost, checker: ts.TypeChecker): {[field: string]: string} { - if (!directive.has(field)) { + const metaValues = parseFieldArrayValue(directive, field, reflector, checker); + if (!metaValues) { return EMPTY_OBJECT; } - // Resolve the field of interest from the directive metadata to a string[]. - const metaValues = staticallyResolve(directive.get(field) !, reflector, checker); - if (!isStringArrayOrDie(metaValues, field)) { - throw new Error(`Failed to resolve @Directive.${field}`); - } - return metaValues.reduce( (results, value) => { // Either the value is 'field' or 'field: property'. In the first case, `property` will diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts index 7c6039a4c3..09726efddb 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {InitialStylingFlags} from '@angular/compiler/src/core'; +import {InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core'; import {MockDirectory, setup} from '@angular/compiler/test/aot/test_util'; import {compile, expectEmit} from './mock_compile'; @@ -18,6 +18,89 @@ describe('compiler compliance: styling', () => { compileAnimations: false, }); + describe('@Component.styles', () => { + it('should pass in the component metadata styles into the component definition and shim them using style encapsulation', + () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: "my-component", + styles: ["div.foo { color: red; }", ":host p:nth-child(even) { --webkit-transition: 1s linear all; }"], + template: "..." + }) + export class MyComponent { + } + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + const template = + 'styles: ["div.foo[_ngcontent-%COMP%] { color: red; }", "[_nghost-%COMP%] p[_ngcontent-%COMP%]:nth-child(even) { --webkit-transition: 1s linear all; }"]'; + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect template'); + }); + + it('should pass in styles, but skip shimming the styles if the view encapsulation signals not to', + () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: "my-component", + encapsulation: ${ViewEncapsulation.None}, + styles: ["div.tall { height: 123px; }", ":host.small p { height:5px; }"], + template: "..." + }) + export class MyComponent { + } + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + const template = 'div.tall { height: 123px; }", ":host.small p { height:5px; }'; + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect template'); + }); + + it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to native', + () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + encapsulation: ${ViewEncapsulation.Native}, + selector: "my-component", + styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"], + template: "..." + }) + export class MyComponent { + } + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + const template = 'div.cool { color: blue; }", ":host.nice p { color: gold; }'; + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect template'); + }); + }); + describe('[style] and [style.prop]', () => { it('should create style instructions on the element', () => { const files = { diff --git a/packages/compiler/src/compile_metadata.ts b/packages/compiler/src/compile_metadata.ts index faf8737b4d..fe00577af0 100644 --- a/packages/compiler/src/compile_metadata.ts +++ b/packages/compiler/src/compile_metadata.ts @@ -188,6 +188,7 @@ export class CompileStylesheetMetadata { export interface CompileTemplateSummary { ngContentSelectors: string[]; encapsulation: ViewEncapsulation|null; + styles: string[]; } /** @@ -243,6 +244,7 @@ export class CompileTemplateMetadata { return { ngContentSelectors: this.ngContentSelectors, encapsulation: this.encapsulation, + styles: this.styles }; } } diff --git a/packages/compiler/src/render3/view/api.ts b/packages/compiler/src/render3/view/api.ts index aca42ed37f..a91b3a2246 100644 --- a/packages/compiler/src/render3/view/api.ts +++ b/packages/compiler/src/render3/view/api.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {ViewEncapsulation} from '../../core'; import * as o from '../../output/output_ast'; import {ParseSourceSpan} from '../../parse_util'; import * as t from '../r3_ast'; @@ -151,6 +152,22 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata { * This is done when the directives contain forward references. */ wrapDirectivesInClosure: boolean; + + /** + * A collection of styling data that will be applied and scoped to the component. + */ + styles: string[]; + + /** + * An encapsulation policy for the template and CSS styles. One of: + * - `ViewEncapsulation.Native`: Use shadow roots. This works only if natively available on the + * platform (note that this is marked the as the "deprecated shadow DOM" as of Angular v6.1. + * - `ViewEncapsulation.Emulated`: Use shimmed CSS that emulates the native behavior. + * - `ViewEncapsulation.None`: Use global CSS without any encapsulation. + * - `ViewEncapsulation.ShadowDom`: Use the latest ShadowDOM API to natively encapsulate styles + * into a shadow root. + */ + encapsulation: ViewEncapsulation; } /** diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index fd8caf9c45..4bf6446258 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -16,6 +16,8 @@ import {LifecycleHooks} from '../../lifecycle_reflector'; import * as o from '../../output/output_ast'; import {typeSourceSpan} from '../../parse_util'; import {CssSelector, SelectorMatcher} from '../../selector'; +import {ShadowCss} from '../../shadow_css'; +import {CONTENT_ATTR, HOST_ATTR} from '../../style_compiler'; import {BindingParser} from '../../template_parser/binding_parser'; import {OutputContext, error} from '../../util'; import {compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory'; @@ -27,6 +29,8 @@ import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata import {BindingScope, TemplateDefinitionBuilder, renderFlagCheckIfStmt} from './template'; import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util'; +const EMPTY_ARRAY: any[] = []; + function baseDirectiveFields( meta: R3DirectiveMetadata, constantPool: ConstantPool, bindingParser: BindingParser): {definitionMap: DefinitionMap, statements: o.Statement[]} { @@ -218,6 +222,15 @@ export function compileComponentFromMetadata( definitionMap.set('pipes', o.literalArr(Array.from(pipesUsed))); } + // e.g. `styles: [str1, str2]` + if (meta.styles && meta.styles.length) { + const styleValues = meta.encapsulation == core.ViewEncapsulation.Emulated ? + compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) : + meta.styles; + const strings = styleValues.map(str => o.literal(str)); + definitionMap.set('styles', o.literalArr(strings)); + } + // On the type side, remove newlines from the selector as it will need to fit into a TypeScript // string literal, which must be on one line. const selectorForType = (meta.selector || '').replace(/\n/g, ''); @@ -287,6 +300,9 @@ export function compileComponentFromRender2( pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx), viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx), wrapDirectivesInClosure: false, + styles: (summary.template && summary.template.styles) || EMPTY_ARRAY, + encapsulation: + (summary.template && summary.template.encapsulation) || core.ViewEncapsulation.Emulated }; const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser); @@ -624,3 +640,8 @@ export function parseHostBindings(host: {[key: string]: string}): { return {attributes, listeners, properties, animations}; } + +function compileStyles(styles: string[], selector: string, hostSelector: string): string[] { + const shadowCss = new ShadowCss(); + return styles.map(style => { return shadowCss !.shimCssText(style, selector, hostSelector); }); +} diff --git a/packages/compiler/src/style_compiler.ts b/packages/compiler/src/style_compiler.ts index 666c501a3e..751a7c3ca8 100644 --- a/packages/compiler/src/style_compiler.ts +++ b/packages/compiler/src/style_compiler.ts @@ -14,8 +14,8 @@ import {UrlResolver} from './url_resolver'; import {OutputContext} from './util'; const COMPONENT_VARIABLE = '%COMP%'; -const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`; -const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`; +export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`; +export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`; export class StylesCompileDependency { constructor( diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 706bc030e1..5f64e5672a 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -9,8 +9,9 @@ import './ng_dev_mode'; import {ChangeDetectionStrategy} from '../change_detection/constants'; -import {Provider, ViewEncapsulation} from '../core'; +import {Provider} from '../di/provider'; import {NgModuleDef, NgModuleDefInternal} from '../metadata/ng_module'; +import {ViewEncapsulation} from '../metadata/view'; import {Type} from '../type'; import {BaseDef, ComponentDefFeature, ComponentDefInternal, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDefFeature, DirectiveDefInternal, DirectiveType, DirectiveTypesOrFactory, PipeDefInternal, PipeType, PipeTypesOrFactory} from './interfaces/definition'; @@ -266,7 +267,8 @@ export function defineComponent(componentDefinition: { const pipeTypes = componentDefinition.pipes !; const directiveTypes = componentDefinition.directives !; const declaredInputs: {[key: string]: string} = {} as any; - const encapsulation = componentDefinition.encapsulation; + const encapsulation = componentDefinition.encapsulation || ViewEncapsulation.Emulated; + const styles: string[] = componentDefinition.styles || EMPTY_ARRAY; const def: ComponentDefInternal = { type: type, diPublic: null, @@ -304,9 +306,10 @@ export function defineComponent(componentDefinition: { data: componentDefinition.data || EMPTY, // TODO(misko): convert ViewEncapsulation into const enum so that it can be used directly in the // next line. Also `None` should be 0 not 2. - encapsulation: encapsulation == null ? 2 /* ViewEncapsulation.None */ : encapsulation, - id: `c${_renderCompCount++}`, - styles: EMPTY_ARRAY, + encapsulation, + providers: EMPTY_ARRAY, + viewProviders: EMPTY_ARRAY, + id: `c${_renderCompCount++}`, styles, }; const feature = componentDefinition.features; feature && feature.forEach((fn) => fn(def)); diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 43e114087c..8c377fd56b 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -255,13 +255,13 @@ export interface ComponentDef extends DirectiveDef, metadata: Component): void { pipes: new Map(), viewQueries: [], wrapDirectivesInClosure: false, + styles: metadata.styles || [], + encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated }, constantPool, makeBindingParser()); const preStatements = [...constantPool.statements, ...res.statements]; 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 e020817d97..f176c906bc 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -27,7 +27,7 @@ "name": "EMPTY$1" }, { - "name": "EMPTY_ARRAY" + "name": "EMPTY_ARRAY$1" }, { "name": "FLAGS" @@ -83,6 +83,9 @@ { "name": "VIEWS" }, + { + "name": "ViewEncapsulation$1" + }, { "name": "_CLEAN_PROMISE" }, diff --git a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json index 43c6ba7cbd..73854eced0 100644 --- a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json @@ -573,10 +573,10 @@ "name": "EMPTY" }, { - "name": "EMPTY_ARRAY$3" + "name": "EMPTY_ARRAY$4" }, { - "name": "EMPTY_ARRAY$4" + "name": "EMPTY_ARRAY$5" }, { "name": "EMPTY_CONTEXT" diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index 9abb429794..46d7e236f9 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -6,7 +6,7 @@ "name": "CIRCULAR$2" }, { - "name": "EMPTY_ARRAY$1" + "name": "EMPTY_ARRAY$2" }, { "name": "GET_PROPERTY_NAME" diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 275423dc22..6f3ddfd2f7 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -45,7 +45,7 @@ "name": "EMPTY$1" }, { - "name": "EMPTY_ARRAY" + "name": "EMPTY_ARRAY$1" }, { "name": "ElementRef" @@ -188,6 +188,9 @@ { "name": "ViewContainerRef$1" }, + { + "name": "ViewEncapsulation$1" + }, { "name": "ViewRef" }, diff --git a/packages/core/test/bundling/todo/index.ts b/packages/core/test/bundling/todo/index.ts index 7584c645df..bb445b9b5b 100644 --- a/packages/core/test/bundling/todo/index.ts +++ b/packages/core/test/bundling/todo/index.ts @@ -9,7 +9,7 @@ import '@angular/core/test/bundling/util/src/reflect_metadata'; import {CommonModule} from '@angular/common'; -import {Component, Injectable, NgModule, ɵrenderComponent as renderComponent} from '@angular/core'; +import {Component, Injectable, NgModule, ViewEncapsulation, ɵrenderComponent as renderComponent} from '@angular/core'; class Todo { editing: boolean; @@ -59,6 +59,7 @@ class TodoStore { @Component({ selector: 'todo-app', // TODO(misko): make this work with `[(ngModel)]` + encapsulation: ViewEncapsulation.None, template: `
diff --git a/packages/core/test/bundling/todo_r2/BUILD.bazel b/packages/core/test/bundling/todo_r2/BUILD.bazel new file mode 100644 index 0000000000..b83424c4e1 --- /dev/null +++ b/packages/core/test/bundling/todo_r2/BUILD.bazel @@ -0,0 +1,116 @@ +package(default_visibility = ["//visibility:public"]) + +load("//tools:defaults.bzl", "ng_module", "ts_library") +load("//tools/http-server:http_server.bzl", "http_server") +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") + +ng_module( + name = "todo", + srcs = ["index.ts"], + tags = ["ivy-only"], + deps = [ + "//packages/common", + "//packages/core", + "//packages/core/test/bundling/util:reflect_metadata", + "//packages/platform-browser", + "//packages/platform-browser-dynamic", + ], +) + +ng_rollup_bundle( + name = "bundle", + # TODO(alexeagle): This is inconsistent. + # We try to teach users to always have their workspace at the start of a + # path, to disambiguate from other workspaces. + # Here, the rule implementation is looking in an execroot where the layout + # has an "external" directory for external dependencies. + # This should probably start with "angular/" and let the rule deal with it. + entry_point = "packages/core/test/bundling/todo_r2/index.js", + tags = ["ivy-only"], + deps = [ + ":todo", + "//packages/common", + "//packages/core", + "//packages/core/test/bundling/util:reflect_metadata", + "//packages/platform-browser", + "//packages/platform-browser-dynamic", + ], +) + +ts_library( + name = "test_lib", + testonly = 1, + srcs = glob(["*_spec.ts"]), + deps = [ + "//packages:types", + "//packages/core", + "//packages/core/testing", + "//packages/platform-browser", + "//packages/platform-browser-dynamic", + "//packages/private/testing", + ], +) + +jasmine_node_test( + name = "test", + data = [ + ":bundle", + ":bundle.js", + ":bundle.min.js", + ":bundle.min_debug.js", + ], + tags = [ + "ivy-jit", + "ivy-local", + "ivy-only", + ], + deps = [":test_lib"], +) + +js_expected_symbol_test( + name = "symbol_test", + src = ":bundle.min_debug.js", + golden = ":bundle.golden_symbols.json", + tags = [ + "ivy-local", + "ivy-only", + ], +) + +genrule( + name = "tslib", + srcs = [ + "@angular_deps//:node_modules/tslib/tslib.js", + ], + outs = [ + "tslib.js", + ], + cmd = "cp $< $@", +) + +ts_devserver( + name = "devserver", + entry_module = "angular/packages/core/test/bundling/todo_r2/index", + serving_path = "/bundle.min.js", + static_files = [ + "index.html", + ":tslib", + "todo.css", + "base.css", + ], + deps = [":todo"], +) + +http_server( + name = "prodserver", + data = [ + "base.css", + "index.html", + "todo.css", + ":bundle.min.js.br", + ":bundle.min_debug.js", + ], +) diff --git a/packages/core/test/bundling/todo_r2/base.css b/packages/core/test/bundling/todo_r2/base.css new file mode 100644 index 0000000000..9f6ac1bd74 --- /dev/null +++ b/packages/core/test/bundling/todo_r2/base.css @@ -0,0 +1,141 @@ +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #c5c5c5; + border-bottom: 1px dashed #f7f7f7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +#issue-count { + display: none; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + transition-property: left; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + padding-left: 300px; + } + + .learn-bar > .learn { + left: 8px; + } +} \ No newline at end of file diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json new file mode 100644 index 0000000000..1a4e32d3f5 --- /dev/null +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -0,0 +1,2402 @@ +[ + { + "name": "ACTIVE_INDEX" + }, + { + "name": "ADD_EVENT_LISTENER" + }, + { + "name": "ALLOW_MULTIPLE_PLATFORMS" + }, + { + "name": "ANGULAR" + }, + { + "name": "APPLICATION_MODULE_PROVIDERS" + }, + { + "name": "APP_BOOTSTRAP_LISTENER" + }, + { + "name": "APP_ID" + }, + { + "name": "APP_ID_RANDOM_PROVIDER" + }, + { + "name": "APP_INITIALIZER" + }, + { + "name": "APP_ROOT" + }, + { + "name": "AT_CHARCODE" + }, + { + "name": "AnonymousSubject" + }, + { + "name": "ApplicationInitStatus" + }, + { + "name": "ApplicationModule" + }, + { + "name": "ApplicationRef" + }, + { + "name": "AsyncPipe" + }, + { + "name": "BINDING_INDEX" + }, + { + "name": "BLOOM_MASK" + }, + { + "name": "BROWSER_MODULE_PROVIDERS" + }, + { + "name": "BROWSER_SANITIZATION_PROVIDERS" + }, + { + "name": "BrowserDomAdapter" + }, + { + "name": "BrowserGetTestability" + }, + { + "name": "BrowserModule" + }, + { + "name": "BrowserPlatformLocation" + }, + { + "name": "CIRCULAR" + }, + { + "name": "CIRCULAR$1" + }, + { + "name": "CIRCULAR$2" + }, + { + "name": "CLEANUP" + }, + { + "name": "CLEAN_PROMISE" + }, + { + "name": "COMMON_DIRECTIVES" + }, + { + "name": "COMMON_PIPES" + }, + { + "name": "COMPONENT_FACTORY_RESOLVER" + }, + { + "name": "COMPONENT_REGEX" + }, + { + "name": "COMPONENT_VARIABLE$1" + }, + { + "name": "CONTAINER_INDEX" + }, + { + "name": "CONTENT_ATTR$1" + }, + { + "name": "CONTEXT" + }, + { + "name": "CORE_TOKENS" + }, + { + "name": "CORE_TOKENS_GLOBAL_NAME" + }, + { + "name": "CURRENCIES_EN" + }, + { + "name": "CURRENCY_CHAR" + }, + { + "name": "ChangeDetectionStrategy" + }, + { + "name": "ChangeDetectorRef" + }, + { + "name": "CommonModule" + }, + { + "name": "Compiler" + }, + { + "name": "CompilerFactory" + }, + { + "name": "ComponentFactory" + }, + { + "name": "ComponentFactory$1" + }, + { + "name": "ComponentFactoryBoundToModule" + }, + { + "name": "ComponentFactoryResolver" + }, + { + "name": "ComponentFactoryResolver$1" + }, + { + "name": "ComponentRef" + }, + { + "name": "ComponentRef$1" + }, + { + "name": "ConnectableSubscriber" + }, + { + "name": "Console" + }, + { + "name": "CurrencyPipe" + }, + { + "name": "DATA_URL_PATTERN" + }, + { + "name": "DATE_FORMATS" + }, + { + "name": "DATE_FORMATS_SPLIT" + }, + { + "name": "DECIMAL_SEP" + }, + { + "name": "DECLARATION_VIEW" + }, + { + "name": "DEFAULT_NB_OF_CURRENCY_DIGITS" + }, + { + "name": "DEPRECATED_PLURAL_FN" + }, + { + "name": "DIGIT_CHAR" + }, + { + "name": "DIRECTIVES" + }, + { + "name": "DOCUMENT" + }, + { + "name": "DOCUMENT$1" + }, + { + "name": "DOM_KEY_LOCATION_NUMPAD" + }, + { + "name": "DatePipe" + }, + { + "name": "DateType" + }, + { + "name": "DecimalPipe" + }, + { + "name": "DefaultDomRenderer2" + }, + { + "name": "DefaultIterableDiffer" + }, + { + "name": "DefaultIterableDifferFactory" + }, + { + "name": "DefaultKeyValueDiffer" + }, + { + "name": "DefaultKeyValueDifferFactory" + }, + { + "name": "DomEventsPlugin" + }, + { + "name": "DomRendererFactory2" + }, + { + "name": "DomSanitizer" + }, + { + "name": "DomSanitizerImpl" + }, + { + "name": "DomSharedStylesHost" + }, + { + "name": "ELEMENT_PROBE_PROVIDERS" + }, + { + "name": "EMPTY" + }, + { + "name": "EMPTY$1" + }, + { + "name": "EMPTY_ARRAY$1" + }, + { + "name": "EMPTY_ARRAY$2" + }, + { + "name": "EMPTY_PAYLOAD" + }, + { + "name": "ERROR_COMPONENT" + }, + { + "name": "ERROR_DEBUG_CONTEXT" + }, + { + "name": "ERROR_LOGGER" + }, + { + "name": "ERROR_ORIGINAL_ERROR" + }, + { + "name": "EVENT_MANAGER_PLUGINS" + }, + { + "name": "EVENT_NAMES" + }, + { + "name": "ElementRef" + }, + { + "name": "ElementRef$1" + }, + { + "name": "EmulatedEncapsulationDomRenderer2" + }, + { + "name": "ErrorHandler" + }, + { + "name": "EventEmitter" + }, + { + "name": "EventManager" + }, + { + "name": "EventManagerPlugin" + }, + { + "name": "FALSE" + }, + { + "name": "FLAGS" + }, + { + "name": "FormStyle" + }, + { + "name": "FormatWidth" + }, + { + "name": "GET_PROPERTY_NAME" + }, + { + "name": "GROUP_SEP" + }, + { + "name": "HAMMER_GESTURE_CONFIG" + }, + { + "name": "HAMMER_LOADER" + }, + { + "name": "HEADER_FILLER" + }, + { + "name": "HEADER_OFFSET" + }, + { + "name": "HOST_ATTR$1" + }, + { + "name": "HOST_NODE" + }, + { + "name": "HammerGestureConfig" + }, + { + "name": "HammerGesturesPlugin" + }, + { + "name": "I18nPluralPipe" + }, + { + "name": "I18nSelectPipe" + }, + { + "name": "IDENT" + }, + { + "name": "INJECTOR" + }, + { + "name": "INJECTOR$1" + }, + { + "name": "INSPECT_GLOBAL_NAME" + }, + { + "name": "ISO8601_DATE_REGEX" + }, + { + "name": "InertBodyHelper" + }, + { + "name": "Inject" + }, + { + "name": "InjectionToken" + }, + { + "name": "Injector" + }, + { + "name": "InnerSubscriber" + }, + { + "name": "IterableChangeRecord_" + }, + { + "name": "IterableDiffers" + }, + { + "name": "JANUARY" + }, + { + "name": "JsonPipe" + }, + { + "name": "KeyEventsPlugin" + }, + { + "name": "KeyValueChangeRecord_" + }, + { + "name": "KeyValueDiffers" + }, + { + "name": "KeyValuePipe" + }, + { + "name": "LOCALE_DATA" + }, + { + "name": "LOCALE_ID" + }, + { + "name": "LifecycleHooksFeature" + }, + { + "name": "LowerCasePipe" + }, + { + "name": "MAX_DIGITS" + }, + { + "name": "MODIFIER_KEYS" + }, + { + "name": "MODIFIER_KEY_GETTERS" + }, + { + "name": "MULTI_PROVIDER_FN" + }, + { + "name": "MapOperator" + }, + { + "name": "MapSubscriber" + }, + { + "name": "MergeMapOperator" + }, + { + "name": "MergeMapSubscriber" + }, + { + "name": "MulticastOperator" + }, + { + "name": "NAMED_FORMATS" + }, + { + "name": "NAMESPACE_URIS" + }, + { + "name": "NATIVE_ADD_LISTENER" + }, + { + "name": "NATIVE_REMOVE_LISTENER" + }, + { + "name": "NEW_LINE" + }, + { + "name": "NEXT" + }, + { + "name": "NG_ELEMENT_ID" + }, + { + "name": "NG_HOST_SYMBOL" + }, + { + "name": "NG_PROJECT_AS_ATTR_NAME" + }, + { + "name": "NG_TEMP_TOKEN_PATH" + }, + { + "name": "NG_TOKEN_PATH" + }, + { + "name": "NON_ALPHANUMERIC_REGEXP" + }, + { + "name": "NOT_YET" + }, + { + "name": "NO_CHANGE" + }, + { + "name": "NO_NEW_LINE" + }, + { + "name": "NULL_INJECTOR" + }, + { + "name": "NULL_INJECTOR$2" + }, + { + "name": "NUMBER_FORMAT_REGEXP" + }, + { + "name": "NgClass" + }, + { + "name": "NgComponentOutlet" + }, + { + "name": "NgForOf" + }, + { + "name": "NgForOfContext" + }, + { + "name": "NgIf" + }, + { + "name": "NgIfContext" + }, + { + "name": "NgLocaleLocalization" + }, + { + "name": "NgLocalization" + }, + { + "name": "NgModuleFactory" + }, + { + "name": "NgModuleFactory$1" + }, + { + "name": "NgModuleRef" + }, + { + "name": "NgModuleRef$1" + }, + { + "name": "NgOnChangesFeature" + }, + { + "name": "NgPlural" + }, + { + "name": "NgPluralCase" + }, + { + "name": "NgProbeToken" + }, + { + "name": "NgStyle" + }, + { + "name": "NgSwitch" + }, + { + "name": "NgSwitchCase" + }, + { + "name": "NgSwitchDefault" + }, + { + "name": "NgTemplateOutlet" + }, + { + "name": "NgZone" + }, + { + "name": "NodeInjector" + }, + { + "name": "NoopNgZone" + }, + { + "name": "NullInjector" + }, + { + "name": "NumberFormatStyle" + }, + { + "name": "NumberSymbol" + }, + { + "name": "OPTIONAL_END_TAG_BLOCK_ELEMENTS" + }, + { + "name": "OPTIONAL_END_TAG_ELEMENTS" + }, + { + "name": "OPTIONAL_END_TAG_INLINE_ELEMENTS" + }, + { + "name": "ObjectUnsubscribedError" + }, + { + "name": "Observable" + }, + { + "name": "ObservableStrategy" + }, + { + "name": "Optional" + }, + { + "name": "OuterSubscriber" + }, + { + "name": "PARAMETERS" + }, + { + "name": "PARENT" + }, + { + "name": "PATTERN_SEP" + }, + { + "name": "PERCENT_CHAR" + }, + { + "name": "PLATFORM_BROWSER_ID" + }, + { + "name": "PLATFORM_ID" + }, + { + "name": "PLATFORM_INITIALIZER" + }, + { + "name": "PLATFORM_SERVER_ID" + }, + { + "name": "PRIVATE_PREFIX" + }, + { + "name": "PercentPipe" + }, + { + "name": "PlatformLocation" + }, + { + "name": "PlatformRef" + }, + { + "name": "Plural" + }, + { + "name": "PublicFeature" + }, + { + "name": "QUERIES" + }, + { + "name": "R3Injector" + }, + { + "name": "REMOVE_EVENT_LISTENER" + }, + { + "name": "RENDERER" + }, + { + "name": "RENDER_PARENT" + }, + { + "name": "ROOT_CONTEXT" + }, + { + "name": "RecordViewTuple" + }, + { + "name": "RefCountOperator" + }, + { + "name": "RefCountSubscriber" + }, + { + "name": "Renderer2" + }, + { + "name": "RendererFactory2" + }, + { + "name": "RendererStyleFlags2" + }, + { + "name": "RendererStyleFlags3" + }, + { + "name": "RootViewRef" + }, + { + "name": "SAFE_STYLE_VALUE" + }, + { + "name": "SAFE_URL_PATTERN" + }, + { + "name": "SANITIZER" + }, + { + "name": "SCHEDULER" + }, + { + "name": "SERVER_TRANSITION_PROVIDERS" + }, + { + "name": "SOURCE" + }, + { + "name": "SRCSET_ATTRS" + }, + { + "name": "SURROGATE_PAIR_REGEXP" + }, + { + "name": "SafeHtmlImpl" + }, + { + "name": "SafeResourceUrlImpl" + }, + { + "name": "SafeScriptImpl" + }, + { + "name": "SafeStyleImpl" + }, + { + "name": "SafeSubscriber" + }, + { + "name": "SafeUrlImpl" + }, + { + "name": "SafeValueImpl" + }, + { + "name": "Sanitizer" + }, + { + "name": "SanitizingHtmlSerializer" + }, + { + "name": "SecurityContext$1" + }, + { + "name": "Self" + }, + { + "name": "ShadowDomRenderer" + }, + { + "name": "SharedStylesHost" + }, + { + "name": "SimpleChange" + }, + { + "name": "SkipSelf" + }, + { + "name": "SlicePipe" + }, + { + "name": "StaticInjector" + }, + { + "name": "Subject" + }, + { + "name": "SubjectSubscriber" + }, + { + "name": "SubjectSubscription" + }, + { + "name": "Subscriber" + }, + { + "name": "Subscription" + }, + { + "name": "SwitchView" + }, + { + "name": "TAIL" + }, + { + "name": "THROW_IF_NOT_FOUND" + }, + { + "name": "THURSDAY" + }, + { + "name": "TRANSITION_ID" + }, + { + "name": "TVIEW" + }, + { + "name": "TemplateRef" + }, + { + "name": "TemplateRef$1" + }, + { + "name": "Testability" + }, + { + "name": "TestabilityRegistry" + }, + { + "name": "TitleCasePipe" + }, + { + "name": "ToDoAppComponent" + }, + { + "name": "ToDoAppComponent_footer_Template_6" + }, + { + "name": "ToDoAppComponent_footer_button_Template_5" + }, + { + "name": "ToDoAppComponent_section_Template_5" + }, + { + "name": "ToDoAppComponent_section_input_Template_1" + }, + { + "name": "ToDoAppComponent_section_li_Template_3" + }, + { + "name": "ToDoAppComponent_section_li_input_Template_6" + }, + { + "name": "ToDoAppModule" + }, + { + "name": "Todo" + }, + { + "name": "TodoStore" + }, + { + "name": "TranslationType" + }, + { + "name": "TranslationWidth" + }, + { + "name": "URI_ATTRS" + }, + { + "name": "URL_RE" + }, + { + "name": "USE_VALUE$1" + }, + { + "name": "UnsubscriptionError" + }, + { + "name": "UpperCasePipe" + }, + { + "name": "VALID_ATTRS" + }, + { + "name": "VALID_ELEMENTS" + }, + { + "name": "VIEWS" + }, + { + "name": "VOID_ELEMENTS" + }, + { + "name": "Version$1" + }, + { + "name": "ViewContainerRef" + }, + { + "name": "ViewContainerRef$1" + }, + { + "name": "ViewEncapsulation$1" + }, + { + "name": "ViewRef" + }, + { + "name": "WRAP_RENDERER_FACTORY2" + }, + { + "name": "WrappedValue" + }, + { + "name": "ZERO_CHAR" + }, + { + "name": "ZoneWidth" + }, + { + "name": "_CLEAN_PROMISE" + }, + { + "name": "_DOM" + }, + { + "name": "_DuplicateItemRecordList" + }, + { + "name": "_DuplicateMap" + }, + { + "name": "_INTERPOLATION_REGEXP" + }, + { + "name": "_NullComponentFactoryResolver" + }, + { + "name": "_THROW_IF_NOT_FOUND" + }, + { + "name": "__assign" + }, + { + "name": "__extends" + }, + { + "name": "__extends$1" + }, + { + "name": "__extends$2" + }, + { + "name": "__extends$3" + }, + { + "name": "__extends$4" + }, + { + "name": "__extends$5" + }, + { + "name": "__extends$6" + }, + { + "name": "__extends$7" + }, + { + "name": "__extends$q" + }, + { + "name": "__extends$r" + }, + { + "name": "__extends$s" + }, + { + "name": "__extends$u" + }, + { + "name": "__read" + }, + { + "name": "__self" + }, + { + "name": "__spread" + }, + { + "name": "__symbol__" + }, + { + "name": "__values" + }, + { + "name": "__window" + }, + { + "name": "_appIdRandomProviderFactory" + }, + { + "name": "_attrToPropMap" + }, + { + "name": "_c0" + }, + { + "name": "_c1" + }, + { + "name": "_c10" + }, + { + "name": "_c11" + }, + { + "name": "_c12" + }, + { + "name": "_c13" + }, + { + "name": "_c14" + }, + { + "name": "_c15" + }, + { + "name": "_c16" + }, + { + "name": "_c17" + }, + { + "name": "_c18" + }, + { + "name": "_c19" + }, + { + "name": "_c2" + }, + { + "name": "_c3" + }, + { + "name": "_c4" + }, + { + "name": "_c5" + }, + { + "name": "_c6" + }, + { + "name": "_c7" + }, + { + "name": "_c8" + }, + { + "name": "_c9" + }, + { + "name": "_callAndReportToErrorHandler" + }, + { + "name": "_chromeNumKeyPadMap" + }, + { + "name": "_createNgProbe" + }, + { + "name": "_currentInjector" + }, + { + "name": "_currentNamespace" + }, + { + "name": "_devMode" + }, + { + "name": "_document" + }, + { + "name": "_enable_super_gross_mode_that_will_cause_bad_things" + }, + { + "name": "_getComponentHostLElementNode" + }, + { + "name": "_global" + }, + { + "name": "_iterableDiffersFactory" + }, + { + "name": "_keyMap" + }, + { + "name": "_keyValueDiffersFactory" + }, + { + "name": "_localeFactory" + }, + { + "name": "_nativeNodeToDebugNode" + }, + { + "name": "_ngProbeTokensToMap" + }, + { + "name": "_observableStrategy" + }, + { + "name": "_promiseStrategy" + }, + { + "name": "_randomChar" + }, + { + "name": "_renderCompCount" + }, + { + "name": "_sanitizeHtml" + }, + { + "name": "_sanitizeStyle" + }, + { + "name": "_sanitizeUrl" + }, + { + "name": "_symbolIterator" + }, + { + "name": "_testabilityGetter" + }, + { + "name": "_throwError" + }, + { + "name": "addComponentLogic" + }, + { + "name": "addDateMinutes" + }, + { + "name": "addRemoveViewFromContainer" + }, + { + "name": "addToViewTree" + }, + { + "name": "adjustBlueprintForNewNode" + }, + { + "name": "allocStylingContext" + }, + { + "name": "appInitializerFactory" + }, + { + "name": "appendChild" + }, + { + "name": "assertPlatform" + }, + { + "name": "assertTemplate" + }, + { + "name": "baseDirectiveCreate" + }, + { + "name": "baseElement" + }, + { + "name": "bind" + }, + { + "name": "bindingRootIndex" + }, + { + "name": "bindingUpdated" + }, + { + "name": "blackListedEvents" + }, + { + "name": "bloomAdd" + }, + { + "name": "bloomFindPossibleInjector" + }, + { + "name": "bloomHashBit" + }, + { + "name": "cacheMatchingDirectivesForNode" + }, + { + "name": "cacheMatchingLocalNames" + }, + { + "name": "callHooks" + }, + { + "name": "canInsertNativeChildOfElement" + }, + { + "name": "canInsertNativeChildOfView" + }, + { + "name": "canInsertNativeNode" + }, + { + "name": "checkFullData" + }, + { + "name": "checkNoChanges" + }, + { + "name": "checkNoChangesInRootView" + }, + { + "name": "checkNoChangesMode" + }, + { + "name": "checkNoSyntheticProp" + }, + { + "name": "checkStable" + }, + { + "name": "cleanUpView" + }, + { + "name": "compileNgModuleFactory" + }, + { + "name": "compileNgModuleFactory__PRE_NGCC__" + }, + { + "name": "componentRefresh" + }, + { + "name": "computeDeps" + }, + { + "name": "config" + }, + { + "name": "connectableObservableDescriptor" + }, + { + "name": "connectableProto" + }, + { + "name": "containerInternal" + }, + { + "name": "contextViewData" + }, + { + "name": "convertTimezoneToLocal" + }, + { + "name": "couldBeInjectableType" + }, + { + "name": "createDirectivesAndLocals" + }, + { + "name": "createEmbeddedViewNode" + }, + { + "name": "createInjector" + }, + { + "name": "createLContainer" + }, + { + "name": "createLNode" + }, + { + "name": "createLNodeObject" + }, + { + "name": "createLViewData" + }, + { + "name": "createOutput$1" + }, + { + "name": "createPlatform" + }, + { + "name": "createPlatformFactory" + }, + { + "name": "createRootContext" + }, + { + "name": "createScope" + }, + { + "name": "createStylingContextTemplate" + }, + { + "name": "createTNode" + }, + { + "name": "createTView" + }, + { + "name": "createTextNode" + }, + { + "name": "createViewBlueprint" + }, + { + "name": "createViewQuery" + }, + { + "name": "currentElementNode" + }, + { + "name": "dateGetter" + }, + { + "name": "dateStrGetter" + }, + { + "name": "decoratePreventDefault" + }, + { + "name": "deepForEach" + }, + { + "name": "defaultComparator" + }, + { + "name": "defaultErrorLogger" + }, + { + "name": "defaultIterableDiffers" + }, + { + "name": "defaultKeyValueDiffers" + }, + { + "name": "defineComponent" + }, + { + "name": "defineDirective" + }, + { + "name": "defineInjectable" + }, + { + "name": "defineInjector" + }, + { + "name": "defineNgModule" + }, + { + "name": "definePipe" + }, + { + "name": "destroyLView" + }, + { + "name": "destroyViewTree" + }, + { + "name": "detachView" + }, + { + "name": "detectChanges" + }, + { + "name": "detectChangesInRootView" + }, + { + "name": "detectChangesInternal" + }, + { + "name": "detectWTF" + }, + { + "name": "diPublic" + }, + { + "name": "diPublicInInjector" + }, + { + "name": "directiveCreate" + }, + { + "name": "directiveInject" + }, + { + "name": "domRendererFactory3" + }, + { + "name": "elementClassProp" + }, + { + "name": "elementCreate" + }, + { + "name": "elementEnd" + }, + { + "name": "elementProperty" + }, + { + "name": "elementStart" + }, + { + "name": "elementStyling" + }, + { + "name": "elementStylingApply" + }, + { + "name": "empty" + }, + { + "name": "encodeEntities" + }, + { + "name": "enterView" + }, + { + "name": "errorHandler" + }, + { + "name": "errorObject" + }, + { + "name": "executeHooks" + }, + { + "name": "executeInitAndContentHooks" + }, + { + "name": "executeInitHooks" + }, + { + "name": "executeNodeAction" + }, + { + "name": "executeOnDestroys" + }, + { + "name": "executePipeOnDestroys" + }, + { + "name": "exportNgVar" + }, + { + "name": "extendStatics" + }, + { + "name": "extractDirectiveDef" + }, + { + "name": "extractPipeDef" + }, + { + "name": "extractTime" + }, + { + "name": "findAttrIndexInNode" + }, + { + "name": "findComponentHost" + }, + { + "name": "findDirectiveMatches" + }, + { + "name": "findLocaleData" + }, + { + "name": "firstTemplatePass" + }, + { + "name": "flattenStyles" + }, + { + "name": "flattenUnsubscriptionErrors" + }, + { + "name": "forkInnerZoneWithAngularBehavior" + }, + { + "name": "formatCurrency" + }, + { + "name": "formatDate" + }, + { + "name": "formatDateTime" + }, + { + "name": "formatError" + }, + { + "name": "formatFractionalSeconds" + }, + { + "name": "formatNumber" + }, + { + "name": "formatNumberToLocaleString" + }, + { + "name": "formatPercent" + }, + { + "name": "forwardRef" + }, + { + "name": "from" + }, + { + "name": "fromArray" + }, + { + "name": "fromIterable" + }, + { + "name": "fromObservable" + }, + { + "name": "fromPromise" + }, + { + "name": "generateInitialInputs" + }, + { + "name": "generatePropertyAliases" + }, + { + "name": "getBaseElementHref" + }, + { + "name": "getChildLNode" + }, + { + "name": "getCleanup" + }, + { + "name": "getClosestComponentAncestor" + }, + { + "name": "getClosureSafeProperty" + }, + { + "name": "getCurrencySymbol" + }, + { + "name": "getCurrentSanitizer" + }, + { + "name": "getCurrentView" + }, + { + "name": "getDOM" + }, + { + "name": "getDateFormatter" + }, + { + "name": "getDatePart" + }, + { + "name": "getDateTranslation" + }, + { + "name": "getDebugContext" + }, + { + "name": "getDebugNode" + }, + { + "name": "getErrorLogger" + }, + { + "name": "getFirstThursdayOfYear" + }, + { + "name": "getInitialIndex" + }, + { + "name": "getInitialValue" + }, + { + "name": "getLViewChild" + }, + { + "name": "getLastDefinedValue" + }, + { + "name": "getLocaleCurrencies" + }, + { + "name": "getLocaleDateFormat" + }, + { + "name": "getLocaleDateTimeFormat" + }, + { + "name": "getLocaleDayNames" + }, + { + "name": "getLocaleDayPeriods" + }, + { + "name": "getLocaleEraNames" + }, + { + "name": "getLocaleExtraDayPeriodRules" + }, + { + "name": "getLocaleExtraDayPeriods" + }, + { + "name": "getLocaleId" + }, + { + "name": "getLocaleMonthNames" + }, + { + "name": "getLocaleNumberFormat" + }, + { + "name": "getLocaleNumberSymbol" + }, + { + "name": "getLocalePluralCase" + }, + { + "name": "getLocaleTimeFormat" + }, + { + "name": "getMultiOrSingleIndex" + }, + { + "name": "getMultiStartIndex" + }, + { + "name": "getNamedFormat" + }, + { + "name": "getNextLNode" + }, + { + "name": "getNgZone" + }, + { + "name": "getNullInjector" + }, + { + "name": "getNumberOfCurrencyDigits" + }, + { + "name": "getOrCreateChangeDetectorRef" + }, + { + "name": "getOrCreateContainerRef" + }, + { + "name": "getOrCreateElementRef" + }, + { + "name": "getOrCreateHostChangeDetector" + }, + { + "name": "getOrCreateInjectable" + }, + { + "name": "getOrCreateNodeInjector" + }, + { + "name": "getOrCreateNodeInjectorForNode" + }, + { + "name": "getOrCreateTView" + }, + { + "name": "getOrCreateTemplateRef" + }, + { + "name": "getOriginalError" + }, + { + "name": "getParentLNode" + }, + { + "name": "getParentState" + }, + { + "name": "getPlatform" + }, + { + "name": "getPluralCategory" + }, + { + "name": "getPointers" + }, + { + "name": "getPreviousIndex" + }, + { + "name": "getPreviousOrParentNode" + }, + { + "name": "getPromiseCtor" + }, + { + "name": "getProp" + }, + { + "name": "getRenderFlags" + }, + { + "name": "getRenderParent" + }, + { + "name": "getRenderer" + }, + { + "name": "getRendererFactory" + }, + { + "name": "getRootView" + }, + { + "name": "getStyleSanitizer" + }, + { + "name": "getStylingContext" + }, + { + "name": "getSymbolIterator" + }, + { + "name": "getSymbolIterator$1" + }, + { + "name": "getTViewCleanup" + }, + { + "name": "getTemplateContent" + }, + { + "name": "getThursdayThisWeek" + }, + { + "name": "getTypeNameForDebugging" + }, + { + "name": "getTypeNameForDebugging$1" + }, + { + "name": "getValue" + }, + { + "name": "globalListener" + }, + { + "name": "hasBalancedQuotes" + }, + { + "name": "hasDeps" + }, + { + "name": "hasOnDestroy" + }, + { + "name": "hasValueChanged" + }, + { + "name": "hostElement" + }, + { + "name": "hostReportError" + }, + { + "name": "identity" + }, + { + "name": "initChangeDetectorIfExisting" + }, + { + "name": "initDomAdapter" + }, + { + "name": "inject" + }, + { + "name": "injectArgs" + }, + { + "name": "injectAttribute" + }, + { + "name": "injectChangeDetectorRef" + }, + { + "name": "injectElementRef" + }, + { + "name": "injectTemplateRef" + }, + { + "name": "injectViewContainerRef" + }, + { + "name": "injectableDefRecord" + }, + { + "name": "insertView" + }, + { + "name": "inspectNativeElement" + }, + { + "name": "instantiateDirectivesDirectly" + }, + { + "name": "interpolation1" + }, + { + "name": "invalidPipeArgumentError" + }, + { + "name": "invertObject" + }, + { + "name": "isArray" + }, + { + "name": "isArrayLike" + }, + { + "name": "isBlackListedEvent" + }, + { + "name": "isComponent" + }, + { + "name": "isContentQueryHost" + }, + { + "name": "isContextDirty" + }, + { + "name": "isCssClassMatching" + }, + { + "name": "isDOMParserAvailable" + }, + { + "name": "isDate$1" + }, + { + "name": "isDevMode" + }, + { + "name": "isDifferent" + }, + { + "name": "isDirty" + }, + { + "name": "isEmpty$1" + }, + { + "name": "isExistingProvider" + }, + { + "name": "isFactoryProvider" + }, + { + "name": "isFunction" + }, + { + "name": "isIterable" + }, + { + "name": "isJsObject" + }, + { + "name": "isListLikeIterable" + }, + { + "name": "isNodeMatchingSelector" + }, + { + "name": "isNodeMatchingSelectorList" + }, + { + "name": "isObject" + }, + { + "name": "isObservable" + }, + { + "name": "isObservable$1" + }, + { + "name": "isPlatformServer" + }, + { + "name": "isPositive" + }, + { + "name": "isProceduralRenderer" + }, + { + "name": "isPromise$1" + }, + { + "name": "isPromise$2" + }, + { + "name": "isScheduler" + }, + { + "name": "isTemplateElement" + }, + { + "name": "isTrustedSubscriber" + }, + { + "name": "isTypeProvider" + }, + { + "name": "isValueProvider" + }, + { + "name": "isoStringToDate" + }, + { + "name": "iterateListLike" + }, + { + "name": "iterator" + }, + { + "name": "keyValDiff" + }, + { + "name": "leave" + }, + { + "name": "leaveView" + }, + { + "name": "listener" + }, + { + "name": "load" + }, + { + "name": "loadElement" + }, + { + "name": "loadElementInternal" + }, + { + "name": "loadInternal" + }, + { + "name": "localeEn" + }, + { + "name": "locateHostElement" + }, + { + "name": "looseIdentical" + }, + { + "name": "makeKeyValuePair" + }, + { + "name": "makeMetadataCtor" + }, + { + "name": "makeParamDecorator" + }, + { + "name": "makeRecord" + }, + { + "name": "map" + }, + { + "name": "markDirtyIfOnPush" + }, + { + "name": "markViewDirty" + }, + { + "name": "merge" + }, + { + "name": "merge$1" + }, + { + "name": "mergeAll" + }, + { + "name": "mergeMap" + }, + { + "name": "multiProviderMixError" + }, + { + "name": "multicast" + }, + { + "name": "namespaceHTML" + }, + { + "name": "nativeInsertBefore" + }, + { + "name": "nativeNodeLocalRefExtractor" + }, + { + "name": "nextContext" + }, + { + "name": "nextNgElementId" + }, + { + "name": "noComponentFactoryError" + }, + { + "name": "noop$1" + }, + { + "name": "noop$2" + }, + { + "name": "noopScope" + }, + { + "name": "observable" + }, + { + "name": "onChangesWrapper" + }, + { + "name": "onEnter" + }, + { + "name": "onLeave" + }, + { + "name": "optionsReducer" + }, + { + "name": "padNumber" + }, + { + "name": "parseCookieValue" + }, + { + "name": "parseIntAutoRadix$1" + }, + { + "name": "parseNumber" + }, + { + "name": "parseNumberFormat" + }, + { + "name": "pipeFromArray" + }, + { + "name": "platformBrowser" + }, + { + "name": "platformCore" + }, + { + "name": "plural" + }, + { + "name": "pointers" + }, + { + "name": "prepareInitialFlag" + }, + { + "name": "projectionNodeStack" + }, + { + "name": "promise" + }, + { + "name": "providerToRecord" + }, + { + "name": "queueComponentIndexForCheck" + }, + { + "name": "queueContentHooks" + }, + { + "name": "queueDestroyHooks" + }, + { + "name": "queueHostBindingForCheck" + }, + { + "name": "queueInitHooks" + }, + { + "name": "queueLifecycleHooks" + }, + { + "name": "queueViewHooks" + }, + { + "name": "readElementValue" + }, + { + "name": "recursivelyProcessProviders" + }, + { + "name": "refCount" + }, + { + "name": "reference" + }, + { + "name": "refreshChildComponents" + }, + { + "name": "refreshContentQueries" + }, + { + "name": "refreshDescendantViews" + }, + { + "name": "refreshDynamicEmbeddedViews" + }, + { + "name": "relativePath" + }, + { + "name": "remove" + }, + { + "name": "removeListeners" + }, + { + "name": "removeView" + }, + { + "name": "renderComponentOrTemplate" + }, + { + "name": "renderEmbeddedTemplate" + }, + { + "name": "renderStyling" + }, + { + "name": "resetApplicationState" + }, + { + "name": "resolveDirective" + }, + { + "name": "resolveForwardRef$1" + }, + { + "name": "resolveProvider" + }, + { + "name": "resolveToken" + }, + { + "name": "restoreView" + }, + { + "name": "roundNumber" + }, + { + "name": "rxSubscriber" + }, + { + "name": "sameHostView" + }, + { + "name": "sanitizeSrcset" + }, + { + "name": "saveNameToExportMap" + }, + { + "name": "saveResolvedLocalsInData" + }, + { + "name": "scheduleMicroTask" + }, + { + "name": "scheduleTick" + }, + { + "name": "searchMatchesQueuedForCreation" + }, + { + "name": "setClass" + }, + { + "name": "setContextDirty" + }, + { + "name": "setCurrentInjector" + }, + { + "name": "setDirty" + }, + { + "name": "setFlag" + }, + { + "name": "setHostBindings" + }, + { + "name": "setInputsForProperty" + }, + { + "name": "setInputsFromAttrs" + }, + { + "name": "setProp" + }, + { + "name": "setRootDomAdapter" + }, + { + "name": "setStyle" + }, + { + "name": "setTestabilityGetter" + }, + { + "name": "setUpAttributes" + }, + { + "name": "setValue" + }, + { + "name": "share" + }, + { + "name": "shareSubjectFactory" + }, + { + "name": "shimContentAttribute" + }, + { + "name": "shimHostAttribute" + }, + { + "name": "staticError" + }, + { + "name": "stopMethodSymbol" + }, + { + "name": "stopSymbol" + }, + { + "name": "storeCleanupFn" + }, + { + "name": "storeCleanupWithContext" + }, + { + "name": "strToNumber" + }, + { + "name": "stringify$1" + }, + { + "name": "stringify$2" + }, + { + "name": "subscribeTo" + }, + { + "name": "subscribeToArray" + }, + { + "name": "subscribeToIterable" + }, + { + "name": "subscribeToObservable" + }, + { + "name": "subscribeToPromise" + }, + { + "name": "subscribeToResult" + }, + { + "name": "supportsState" + }, + { + "name": "symbolNames" + }, + { + "name": "tagSet" + }, + { + "name": "template" + }, + { + "name": "text" + }, + { + "name": "textBinding" + }, + { + "name": "throwCyclicDependencyError" + }, + { + "name": "throwErrorIfNoChangesMode" + }, + { + "name": "throwMultipleComponentError" + }, + { + "name": "tickRootContext" + }, + { + "name": "timeZoneGetter" + }, + { + "name": "timezoneToOffset" + }, + { + "name": "toDate" + }, + { + "name": "toPercent" + }, + { + "name": "toRefArray" + }, + { + "name": "toSubscriber" + }, + { + "name": "trackByIdentity" + }, + { + "name": "tryCatch" + }, + { + "name": "tryCatcher" + }, + { + "name": "tryResolveToken" + }, + { + "name": "u" + }, + { + "name": "unicodeWordMatch" + }, + { + "name": "updateClassProp" + }, + { + "name": "updateStyleProp" + }, + { + "name": "updateViewQuery" + }, + { + "name": "valueExists" + }, + { + "name": "viewAttached" + }, + { + "name": "walkLNodeTree" + }, + { + "name": "walkUpViews" + }, + { + "name": "weekGetter" + }, + { + "name": "wrapListenerWithDirtyAndDefault" + }, + { + "name": "wrapListenerWithDirtyLogic" + }, + { + "name": "wtfCreateScope" + }, + { + "name": "wtfEnabled" + }, + { + "name": "wtfLeave" + } +] \ No newline at end of file diff --git a/packages/core/test/bundling/todo_r2/index.html b/packages/core/test/bundling/todo_r2/index.html new file mode 100644 index 0000000000..5d25aabdeb --- /dev/null +++ b/packages/core/test/bundling/todo_r2/index.html @@ -0,0 +1,57 @@ + + + + + + Angular Todo Example + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/core/test/bundling/todo_r2/index.ts b/packages/core/test/bundling/todo_r2/index.ts new file mode 100644 index 0000000000..16b2404c30 --- /dev/null +++ b/packages/core/test/bundling/todo_r2/index.ts @@ -0,0 +1,165 @@ +/** + * @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 '@angular/core/test/bundling/util/src/reflect_metadata'; + +import {CommonModule} from '@angular/common'; +import {Component, Injectable, NgModule, ɵNgModuleFactory as NgModuleFactory} from '@angular/core'; +import {BrowserModule, platformBrowser} from '@angular/platform-browser'; + +class Todo { + editing: boolean; + + // TODO(issue/24571): remove '!'. + private _title !: string; + get title() { return this._title; } + set title(value: string) { this._title = value.trim(); } + + constructor(title: string, public completed: boolean = false) { + this.editing = false; + this.title = title; + } +} + +@Injectable({providedIn: 'root'}) +class TodoStore { + todos: Array = [ + new Todo('Demonstrate Components'), + new Todo('Demonstrate Structural Directives', true), + new Todo('Demonstrate NgModules'), + new Todo('Demonstrate zoneless change detection'), + new Todo('Demonstrate internationalization'), + ]; + + private getWithCompleted(completed: boolean) { + return this.todos.filter((todo: Todo) => todo.completed === completed); + } + + allCompleted() { return this.todos.length === this.getCompleted().length; } + + setAllTo(completed: boolean) { this.todos.forEach((t: Todo) => t.completed = completed); } + + removeCompleted() { this.todos = this.getWithCompleted(false); } + + getRemaining() { return this.getWithCompleted(false); } + + getCompleted() { return this.getWithCompleted(true); } + + toggleCompletion(todo: Todo) { todo.completed = !todo.completed; } + + remove(todo: Todo) { this.todos.splice(this.todos.indexOf(todo), 1); } + + add(title: string) { this.todos.push(new Todo(title)); } +} + +@Component({ + selector: 'todo-app', + // TODO(misko): make this work with `[(ngModel)]` + styles: [` + .todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; + font-weight:bold; + } + `], + template: ` +
+
+

todos

+ +
+
+ +
    +
  • +
    + + + +
    + +
  • +
+
+
+ + {{todoStore.getRemaining().length}} + {{todoStore.getRemaining().length == 1 ? 'item' : 'items'}} left + + +
+
+ `, + // TODO(misko): switch over to OnPush + // changeDetection: ChangeDetectionStrategy.OnPush +}) +class ToDoAppComponent { + newTodoText = ''; + + constructor(public todoStore: TodoStore) { (window as any).toDoAppComponent = this; } + + stopEditing(todo: Todo, editedTitle: string) { + todo.title = editedTitle; + todo.editing = false; + } + + cancelEditingTodo(todo: Todo) { todo.editing = false; } + + updateEditingTodo(todo: Todo, editedTitle: string) { + editedTitle = editedTitle.trim(); + todo.editing = false; + + if (editedTitle.length === 0) { + return this.todoStore.remove(todo); + } + + todo.title = editedTitle; + } + + editTodo(todo: Todo) { todo.editing = true; } + + removeCompleted() { this.todoStore.removeCompleted(); } + + toggleCompletion(todo: Todo) { this.todoStore.toggleCompletion(todo); } + + remove(todo: Todo) { this.todoStore.remove(todo); } + + addTodo() { + if (this.newTodoText.trim().length) { + this.todoStore.add(this.newTodoText); + this.newTodoText = ''; + } + } +} + +@NgModule({declarations: [ToDoAppComponent], imports: [CommonModule, BrowserModule]}) +class ToDoAppModule { + ngDoBootstrap(app: any) { app.bootstrap(ToDoAppComponent); } +} + +(window as any).waitForApp = + platformBrowser().bootstrapModuleFactory(new NgModuleFactory(ToDoAppModule), {ngZone: 'noop'}); diff --git a/packages/core/test/bundling/todo_r2/todo.css b/packages/core/test/bundling/todo_r2/todo.css new file mode 100644 index 0000000000..5c139f868c --- /dev/null +++ b/packages/core/test/bundling/todo_r2/todo.css @@ -0,0 +1,372 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #f5f5f5; + color: #4d4d4d; + min-width: 230px; + max-width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + font-smoothing: antialiased; + font-weight: 300; +} + +button, +input[type="checkbox"] { + outline: none; +} + +.hidden { + display: none; +} + +.todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +.todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +.new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +.new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); +} + +.main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +label[for='toggle-all'] { + display: none; +} + +.toggle-all { + position: absolute; + top: -55px; + left: -12px; + width: 60px; + height: 34px; + text-align: center; + border: none; /* Mobile Safari */ +} + +.toggle-all:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +.toggle-all:checked:before { + color: #737373; +} + +.todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +.todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +.todo-list li:last-child { + border-bottom: none; +} + +.todo-list li.editing { + border-bottom: none; + padding: 0; +} + +.todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +.todo-list li.editing .view { + display: none; +} + +.todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; /* Mobile Safari */ + -webkit-appearance: none; + appearance: none; +} + +.todo-list li .toggle:after { + content: url('data:image/svg+xml;utf8,'); +} + +.todo-list li .toggle:checked:after { + content: url('data:image/svg+xml;utf8,'); +} + +.todo-list li label { + white-space: pre-line; + word-break: break-all; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +.todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; +} + +.todo-list li .destroy:hover { + color: #af5b5e; +} + +.todo-list li .destroy:after { + content: '×'; +} + +.todo-list li:hover .destroy { + display: block; +} + +.todo-list li .edit { + display: none; +} + +.todo-list li.editing:last-child { + margin-bottom: -1px; +} + +.footer { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +.footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +.todo-count { + float: left; + text-align: left; +} + +.todo-count strong { + font-weight: 300; +} + +.filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +.filters li { + display: inline; +} + +.filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +.filters li a.selected, +.filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +.filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +.clear-completed, +html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; +} + +.clear-completed:hover { + text-decoration: underline; +} + +.info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; +} + +.info p { + line-height: 1; +} + +.info a { + color: inherit; + text-decoration: none; + font-weight: 400; +} + +.info a:hover { + text-decoration: underline; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox +*/ +@media screen and (-webkit-min-device-pixel-ratio:0) { + .toggle-all, + .todo-list li .toggle { + background: none; + } + + .todo-list li .toggle { + height: 40px; + } + + .toggle-all { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +@media (max-width: 430px) { + .footer { + height: 50px; + } + + .filters { + bottom: 10px; + } +} diff --git a/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts b/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts new file mode 100644 index 0000000000..c3e38ea1d1 --- /dev/null +++ b/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts @@ -0,0 +1,51 @@ +/** + * @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 {ɵwhenRendered as whenRendered} from '@angular/core'; +import {withBody} from '@angular/private/testing'; +import * as fs from 'fs'; +import * as path from 'path'; + +const UTF8 = { + encoding: 'utf-8' +}; +const PACKAGE = 'angular/packages/core/test/bundling/todo_r2'; +const BUNDLES = ['bundle.js', 'bundle.min_debug.js', 'bundle.min.js']; + +describe('functional test for todo', () => { + BUNDLES.forEach(bundle => { + describe(bundle, () => { + it('should place styles on the elements within the component', + withBody('', async() => { + require(path.join(PACKAGE, bundle)); + await(window as any).waitForApp; + const toDoAppComponent = (window as any).toDoAppComponent; + await whenRendered(toDoAppComponent); + + const styleContent = findStyleTextForSelector('.todo-list\\\[_ngcontent-\\\w+\\\]'); + expect(styleContent).toMatch(/font-weight:\s*bold;/); + expect(styleContent).toMatch(/color:\s*#d9d9d9;/); + })); + }); + }); +}); + +function findStyleTextForSelector(selector: string): string { + const styles = document.querySelectorAll('head style'); + const matchExp = new RegExp(`${selector}.+?\\\{([\\s\\S]+)\\\}`, 'm'); + for (let i = 0; i < styles.length; i++) { + const styleElement = styles[i]; + const content = styleElement.textContent || ''; + const result = matchExp.exec(content); + if (result && result.length > 1) { + return result[1]; + } + } + + throw new Error(`The CSS Style Rule ${selector} was not found`); +} diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index 4a8044e88a..0957268fce 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -26,6 +26,7 @@ describe('component', () => { static ngComponentDef = defineComponent({ type: CounterComponent, + encapsulation: ViewEncapsulation.None, selectors: [['counter']], consts: 1, vars: 1, @@ -72,6 +73,7 @@ describe('component', () => { constructor(public myService: MyService) {} static ngComponentDef = defineComponent({ type: MyComponent, + encapsulation: ViewEncapsulation.None, selectors: [['my-component']], factory: () => new MyComponent(directiveInject(MyService)), consts: 1, @@ -139,6 +141,7 @@ describe('component with a container', () => { items !: string[]; static ngComponentDef = defineComponent({ type: WrapperComponent, + encapsulation: ViewEncapsulation.None, selectors: [['wrapper']], consts: 1, vars: 0, @@ -188,6 +191,7 @@ describe('encapsulation', () => { class WrapperComponent { static ngComponentDef = defineComponent({ type: WrapperComponent, + encapsulation: ViewEncapsulation.None, selectors: [['wrapper']], consts: 1, vars: 0, @@ -224,6 +228,7 @@ describe('encapsulation', () => { class LeafComponent { static ngComponentDef = defineComponent({ type: LeafComponent, + encapsulation: ViewEncapsulation.None, selectors: [['leaf']], consts: 2, vars: 0, @@ -334,6 +339,7 @@ describe('recursive components', () => { static ngComponentDef = defineComponent({ type: TreeComponent, + encapsulation: ViewEncapsulation.None, selectors: [['tree-comp']], factory: () => new TreeComponent(), consts: 3, @@ -395,6 +401,7 @@ describe('recursive components', () => { static ngComponentDef = defineComponent({ type: NgIfTree, + encapsulation: ViewEncapsulation.None, selectors: [['ng-if-tree']], factory: () => new NgIfTree(), consts: 3, @@ -534,6 +541,7 @@ describe('recursive components', () => { minifiedName !: string; static ngComponentDef = defineComponent({ type: TestInputsComponent, + encapsulation: ViewEncapsulation.None, selectors: [['test-inputs']], inputs: {minifiedName: 'unminifiedName'}, consts: 0, diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 7d4e78687d..309362d546 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -9,10 +9,12 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3'; +import {RendererType2} from '../../src/render/api'; import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di'; import {AttributeMarker, defineComponent, defineDirective, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions'; import {InitialStylingFlags} from '../../src/render3/interfaces/definition'; +import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {HEADER_OFFSET} from '../../src/render3/interfaces/view'; import {sanitizeUrl} from '../../src/sanitization/sanitization'; import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; @@ -1384,6 +1386,32 @@ describe('render3 integration test', () => { }); + describe('component styles', () => { + it('should pass in the component styles directly into the underlying renderer', () => { + class StyledComp { + static ngComponentDef = defineComponent({ + type: StyledComp, + styles: ['div { color: red; }'], + consts: 1, + vars: 0, + encapsulation: 100, + selectors: [['foo']], + factory: () => new StyledComp(), + template: (rf: RenderFlags, ctx: StyledComp) => { + if (rf & RenderFlags.Create) { + element(0, 'div'); + } + } + }); + } + + const rendererFactory = new MockRendererFactory(); + new ComponentFixture(StyledComp, {rendererFactory}); + expect(rendererFactory.lastCapturedType !.styles).toEqual(['div { color: red; }']); + expect(rendererFactory.lastCapturedType !.encapsulation).toEqual(100); + }); + }); + describe('sanitization', () => { it('should sanitize data using the provided sanitization interface', () => { class SanitizationComp { @@ -1447,3 +1475,12 @@ class LocalSanitizer implements Sanitizer { bypassSecurityTrustUrl(value: string) { return new LocalSanitizedValue(value); } } + +class MockRendererFactory implements RendererFactory3 { + lastCapturedType: RendererType2|null = null; + + createRenderer(hostElement: RElement|null, rendererType: RendererType2|null): Renderer3 { + this.lastCapturedType = rendererType; + return domRendererFactory3.createRenderer(hostElement, rendererType); + } +} diff --git a/packages/core/test/render3/renderer_factory_spec.ts b/packages/core/test/render3/renderer_factory_spec.ts index d79dc7ff0d..3e1459c1a5 100644 --- a/packages/core/test/render3/renderer_factory_spec.ts +++ b/packages/core/test/render3/renderer_factory_spec.ts @@ -32,6 +32,7 @@ describe('renderer factory lifecycle', () => { class SomeComponent { static ngComponentDef = defineComponent({ type: SomeComponent, + encapsulation: ViewEncapsulation.None, selectors: [['some-component']], consts: 1, vars: 0, @@ -48,6 +49,7 @@ describe('renderer factory lifecycle', () => { class SomeComponentWhichThrows { static ngComponentDef = defineComponent({ type: SomeComponentWhichThrows, + encapsulation: ViewEncapsulation.None, selectors: [['some-component-with-Error']], consts: 0, vars: 0, @@ -128,6 +130,7 @@ describe('animation renderer factory', () => { class SomeComponent { static ngComponentDef = defineComponent({ type: SomeComponent, + encapsulation: ViewEncapsulation.None, selectors: [['some-component']], consts: 1, vars: 0, @@ -258,6 +261,7 @@ describe('Renderer2 destruction hooks', () => { class SimpleComponent { static ngComponentDef = defineComponent({ type: SimpleComponent, + encapsulation: ViewEncapsulation.None, selectors: [['simple']], consts: 1, vars: 0, diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 617b10e1b7..c072115c5b 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -7,8 +7,10 @@ */ import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core'; +import {ViewEncapsulation} from '../../src/metadata'; import {templateRefExtractor} from '../../src/render3/di'; import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; + import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, loadDirective, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {NgModuleFactory} from '../../src/render3/ng_module_ref'; @@ -261,6 +263,7 @@ describe('ViewContainerRef', () => { testDir !: TestDirective; static ngComponentDef = defineComponent({ type: TestComponent, + encapsulation: ViewEncapsulation.None, selectors: [['test-cmp']], factory: () => new TestComponent(), consts: 4, @@ -332,6 +335,7 @@ describe('ViewContainerRef', () => { testDir !: TestDirective; static ngComponentDef = defineComponent({ type: TestComponent, + encapsulation: ViewEncapsulation.None, selectors: [['test-cmp']], consts: 4, vars: 0, @@ -393,6 +397,7 @@ describe('ViewContainerRef', () => { static ngComponentDef = defineComponent({ type: Child, + encapsulation: ViewEncapsulation.None, selectors: [['child']], factory: () => new Child(), consts: 1, @@ -442,6 +447,7 @@ describe('ViewContainerRef', () => { class SomeComponent { static ngComponentDef = defineComponent({ type: SomeComponent, + encapsulation: ViewEncapsulation.None, selectors: [['some-comp']], factory: () => new SomeComponent(), consts: 6, @@ -509,6 +515,7 @@ describe('ViewContainerRef', () => { static ngComponentDef = defineComponent({ type: Child, + encapsulation: ViewEncapsulation.None, selectors: [['child']], factory: () => child = new Child(), consts: 2, @@ -593,6 +600,7 @@ describe('ViewContainerRef', () => { static ngComponentDef = defineComponent({ type: LoopComp, + encapsulation: ViewEncapsulation.None, selectors: [['loop-comp']], factory: () => new LoopComp(), consts: 1, @@ -906,6 +914,7 @@ describe('ViewContainerRef', () => { class EmbeddedComponent { static ngComponentDef = defineComponent({ type: EmbeddedComponent, + encapsulation: ViewEncapsulation.None, selectors: [['embedded-cmp']], factory: () => new EmbeddedComponent(), consts: 1, @@ -991,6 +1000,7 @@ describe('ViewContainerRef', () => { class EmbeddedComponentWithNgContent { static ngComponentDef = defineComponent({ type: EmbeddedComponentWithNgContent, + encapsulation: ViewEncapsulation.None, selectors: [['embedded-cmp-with-ngcontent']], factory: () => new EmbeddedComponentWithNgContent(), consts: 3, @@ -1030,6 +1040,7 @@ describe('ViewContainerRef', () => { class Reprojector { static ngComponentDef = defineComponent({ type: Reprojector, + encapsulation: ViewEncapsulation.None, selectors: [['reprojector']], factory: () => new Reprojector(), consts: 2, @@ -1150,6 +1161,7 @@ describe('ViewContainerRef', () => { class Child { static ngComponentDef = defineComponent({ type: Child, + encapsulation: ViewEncapsulation.None, selectors: [['child']], factory: () => new Child(), consts: 2, @@ -1177,6 +1189,7 @@ describe('ViewContainerRef', () => { name: string = 'bar'; static ngComponentDef = defineComponent({ type: Parent, + encapsulation: ViewEncapsulation.None, selectors: [['parent']], factory: () => new Parent(), consts: 5, @@ -1227,6 +1240,7 @@ describe('ViewContainerRef', () => { show: boolean = true; static ngComponentDef = defineComponent({ type: ChildWithView, + encapsulation: ViewEncapsulation.None, selectors: [['child-with-view']], factory: () => new ChildWithView(), consts: 3, @@ -1269,6 +1283,7 @@ describe('ViewContainerRef', () => { name: string = 'bar'; static ngComponentDef = defineComponent({ type: Parent, + encapsulation: ViewEncapsulation.None, selectors: [['parent']], factory: () => new Parent(), consts: 7, @@ -1317,6 +1332,7 @@ describe('ViewContainerRef', () => { class ChildWithSelector { static ngComponentDef = defineComponent({ type: ChildWithSelector, + encapsulation: ViewEncapsulation.None, selectors: [['child-with-selector']], factory: () => new ChildWithSelector(), consts: 4, @@ -1350,6 +1366,7 @@ describe('ViewContainerRef', () => { name: string = 'bar'; static ngComponentDef = defineComponent({ type: Parent, + encapsulation: ViewEncapsulation.None, selectors: [['parent']], factory: () => new Parent(), consts: 5, @@ -1402,6 +1419,7 @@ describe('ViewContainerRef', () => { name: string = 'bar'; static ngComponentDef = defineComponent({ type: Parent, + encapsulation: ViewEncapsulation.None, selectors: [['parent']], factory: () => new Parent(), consts: 5, @@ -1468,6 +1486,7 @@ describe('ViewContainerRef', () => { static ngComponentDef = defineComponent({ type: ComponentWithHooks, + encapsulation: ViewEncapsulation.None, selectors: [['hooks']], factory: () => new ComponentWithHooks(), consts: 1, @@ -1602,6 +1621,7 @@ describe('ViewContainerRef', () => { class SomeComponent { static ngComponentDef = defineComponent({ type: SomeComponent, + encapsulation: ViewEncapsulation.None, selectors: [['some-comp']], factory: () => new SomeComponent(), consts: 2,