From 89edfe76a602c2a18f11065ecb2dacfb89a5c896 Mon Sep 17 00:00:00 2001 From: acvi Date: Wed, 13 Jul 2022 08:55:10 +0200 Subject: [PATCH 01/40] docs: describe smallest typo, "an" -> "a" (#46812) PR Close #46812 --- aio/content/guide/testing-components-basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/testing-components-basics.md b/aio/content/guide/testing-components-basics.md index ec55922bc0c5..544a79a4bbcd 100644 --- a/aio/content/guide/testing-components-basics.md +++ b/aio/content/guide/testing-components-basics.md @@ -7,7 +7,7 @@ To adequately test a component, you should test that they work together as inten Such tests require creating the component's host element in the browser DOM, as Angular does, and investigating the component class's interaction with the DOM as described by its template. The Angular `TestBed` facilitates this kind of testing as you'll see in the following sections. -But in many cases, *testing the component class alone*, without DOM involvement, can validate much of the component's behavior in an straightforward, more obvious way. +But in many cases, *testing the component class alone*, without DOM involvement, can validate much of the component's behavior in a straightforward, more obvious way.
From 41253f9c467aa9742ef0b69ddd063043fe0300e2 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 13 Jul 2022 20:19:40 +0200 Subject: [PATCH 02/40] fix(compiler): inputs/outputs incorrectly parsed in jit mode (#46813) The `Directive` and `Component` decorators support `inputs` and `outputs` fields which accept an array in the format of `"someInput"` or `"someInput: someAlias"`, however the parsing during JIT compilation was splitting on commas, not on colons, which resulted in incorrect parsing. E.g. `inputs: ["someInput: someAlias"]` was being parsed into `{"someInput: someAlias": "someInput: someAlias"}` instead of `{someInput: "someAlias"}`. The feature was working by accident, because there's some logic further down in the compiler pipeline that was splitting the strings again. PR Close #46813 --- packages/compiler/src/jit_compiler_facade.ts | 8 ++++---- packages/compiler/src/render3/view/util.ts | 10 +++------- .../src/dynamic/src/upgrade_ng1_adapter.ts | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/compiler/src/jit_compiler_facade.ts b/packages/compiler/src/jit_compiler_facade.ts index 442b6c6f1396..dc56452e82a2 100644 --- a/packages/compiler/src/jit_compiler_facade.ts +++ b/packages/compiler/src/jit_compiler_facade.ts @@ -635,10 +635,10 @@ function isOutput(value: any): value is Output { } function parseInputOutputs(values: string[]): StringMap { - return values.reduce((map, value) => { - const [field, property] = value.split(',').map(piece => piece.trim()); - map[field] = property || field; - return map; + return values.reduce((results, value) => { + const [field, property] = value.split(':', 2).map(str => str.trim()); + results[field] = property || field; + return results; }, {} as StringMap); } diff --git a/packages/compiler/src/render3/view/util.ts b/packages/compiler/src/render3/view/util.ts index d30f2cb5733b..6f683541a799 100644 --- a/packages/compiler/src/render3/view/util.ts +++ b/packages/compiler/src/render3/view/util.ts @@ -175,7 +175,6 @@ function mapToExpression( map: {[key: string]: string|string[]}, keepDeclared?: boolean): o.Expression { return o.literalMap(Object.getOwnPropertyNames(map).map(key => { // canonical syntax: `dirProp: publicProp` - // if there is no `:`, use dirProp = elProp const value = map[key]; let declaredName: string; let publicName: string; @@ -186,12 +185,9 @@ function mapToExpression( minifiedName = key; needsDeclaredName = publicName !== declaredName; } else { - [declaredName, publicName] = splitAtColon(key, [key, value]); - minifiedName = declaredName; - // Only include the declared name if extracted from the key, i.e. the key contains a colon. - // Otherwise the declared name should be omitted even if it is different from the public name, - // as it may have already been minified. - needsDeclaredName = publicName !== declaredName && key.includes(':'); + minifiedName = declaredName = key; + publicName = value; + needsDeclaredName = false; } return { key: minifiedName, diff --git a/packages/upgrade/src/dynamic/src/upgrade_ng1_adapter.ts b/packages/upgrade/src/dynamic/src/upgrade_ng1_adapter.ts index e29000ec6ee2..62d45c0f8231 100644 --- a/packages/upgrade/src/dynamic/src/upgrade_ng1_adapter.ts +++ b/packages/upgrade/src/dynamic/src/upgrade_ng1_adapter.ts @@ -20,6 +20,13 @@ const INITIAL_VALUE = { }; const NOT_SUPPORTED: any = 'NOT_SUPPORTED'; +function getInputPropertyMapName(name: string): string { + return `input_${name}`; +} + +function getOutputPropertyMapName(name: string): string { + return `output_${name}`; +} export class UpgradeNg1ComponentAdapterBuilder { // TODO(issue/24571): remove '!'. @@ -33,7 +40,7 @@ export class UpgradeNg1ComponentAdapterBuilder { propertyMap: {[name: string]: string} = {}; directive: IDirective|null = null; // TODO(issue/24571): remove '!'. - template !: string; + template!: string; constructor(public name: string) { const selector = @@ -76,9 +83,9 @@ export class UpgradeNg1ComponentAdapterBuilder { // QUESTION: What about `=*`? Ignore? Throw? Support? - const inputName = `input_${attrName}`; + const inputName = getInputPropertyMapName(attrName); const inputNameRename = `${inputName}: ${attrName}`; - const outputName = `output_${attrName}`; + const outputName = getOutputPropertyMapName(attrName); const outputNameRename = `${outputName}: ${attrName}`; const outputNameRenameChange = `${outputNameRename}Change`; @@ -225,8 +232,9 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck { const ng1Changes: any = {}; Object.keys(changes).forEach(name => { const change: SimpleChange = changes[name]; - this.setComponentProperty(name, change.currentValue); - ng1Changes[this.propertyMap[name]] = change; + const propertyMapName = getInputPropertyMapName(name); + this.setComponentProperty(propertyMapName, change.currentValue); + ng1Changes[this.propertyMap[propertyMapName]] = change; }); if (isFunction(this.destinationObj!.$onChanges)) { From 8e2fc3e951d4af7d8bdba818bac91aa5aef29019 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Wed, 13 Jul 2022 17:37:45 +0300 Subject: [PATCH 03/40] docs: redirect `/guide/ivy` to the v12 guide (#46820) Since Ivy is the default since v13, the Ivy guide (that used to live at https://angular.io/guide/ivy) has been removed (see #43860). However, there are certain error messages emitted by the CLI that still point to it. This commit address the problem by adding a redirect from https://angular.io/guide/ivy to https://v12.angular.io/guide/ivy (which is the last version that includes the guide). Fixes #46717 PR Close #46820 --- aio/firebase.json | 1 + aio/tests/deployment/shared/URLS_TO_REDIRECT.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/aio/firebase.json b/aio/firebase.json index 09c1b8cca759..89a056181eef 100644 --- a/aio/firebase.json +++ b/aio/firebase.json @@ -75,6 +75,7 @@ {"type": 301, "source": "/getting-started", "destination": "/start"}, {"type": 301, "source": "/getting-started/:rest*", "destination": "/start/:rest*"}, {"type": 301, "source": "/guide/displaying-data", "destination": "/start#template-syntax"}, + {"type": 301, "source": "/guide/ivy", "destination": "https://v12.angular.io/guide/ivy"}, {"type": 301, "source": "/guide/updating-to-version-10", "destination": "https://v10.angular.io/guide/updating-to-version-10"}, {"type": 301, "source": "/guide/updating-to-version-11", "destination": "https://v11.angular.io/guide/updating-to-version-11"}, {"type": 301, "source": "/guide/updating-to-version-12", "destination": "/guide/update-to-latest-version"}, diff --git a/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt b/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt index 8a0adc8324f7..9379d3da1027 100644 --- a/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt +++ b/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt @@ -205,6 +205,7 @@ /guide/displaying-data --> /start#template-syntax /guide/i18n --> /guide/i18n-overview /guide --> /docs +/guide/ivy --> https://v12.angular.io/guide/ivy /guide/learning-angular --> /start /guide/learning-angular.html --> /start /guide/metadata --> /guide/aot-compiler From 481dc8dd2ec46c833d3e885408b8cb6bc417adc4 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Wed, 13 Jul 2022 12:31:37 -0700 Subject: [PATCH 04/40] refactor(core): improve an error message when `ENVIRONMENT_INITIALIZER` is not a multi provider (#46829) Currently if the `ENVIRONMENT_INITIALIZER` token is not configured with `multi: true` flag, the code fails while trying to iterate over the value. This commit checks whether the `ENVIRONMENT_INITIALIZER` token value type is an array and throws a helpful error message. PR Close #46829 --- goldens/public-api/core/errors.md | 2 ++ packages/core/src/di/r3_injector.ts | 8 ++++++++ packages/core/src/errors.ts | 1 + .../test/acceptance/environment_injector_spec.ts | 16 +++++++++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/goldens/public-api/core/errors.md b/goldens/public-api/core/errors.md index 3838e00d62bd..5fc140815380 100644 --- a/goldens/public-api/core/errors.md +++ b/goldens/public-api/core/errors.md @@ -47,6 +47,8 @@ export const enum RuntimeErrorCode { // (undocumented) INVALID_INJECTION_TOKEN = 204, // (undocumented) + INVALID_MULTI_PROVIDER = 209, + // (undocumented) MISSING_GENERATED_DEF = 906, // (undocumented) MISSING_INJECTION_CONTEXT = -203, diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index 6d4e94774381..81cc5a3e1a82 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -247,6 +247,14 @@ export class R3Injector extends EnvironmentInjector { const previousInjectImplementation = setInjectImplementation(undefined); try { const initializers = this.get(ENVIRONMENT_INITIALIZER.multi, EMPTY_ARRAY, InjectFlags.Self); + if (ngDevMode && !Array.isArray(initializers)) { + throw new RuntimeError( + RuntimeErrorCode.INVALID_MULTI_PROVIDER, + 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' + + `(expected an array, but got ${typeof initializers}). ` + + 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' + + '`multi: true` provider.'); + } for (const initializer of initializers) { initializer(); } diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts index 003f795e9771..d517878135d0 100644 --- a/packages/core/src/errors.ts +++ b/packages/core/src/errors.ts @@ -32,6 +32,7 @@ export const enum RuntimeErrorCode { INJECTOR_ALREADY_DESTROYED = 205, PROVIDER_IN_WRONG_CONTEXT = 207, MISSING_INJECTION_TOKEN = 208, + INVALID_MULTI_PROVIDER = 209, // Template Errors MULTIPLE_COMPONENTS_MATCH = -300, diff --git a/packages/core/test/acceptance/environment_injector_spec.ts b/packages/core/test/acceptance/environment_injector_spec.ts index 2ad7eb5e1a06..0c91f074a526 100644 --- a/packages/core/test/acceptance/environment_injector_spec.ts +++ b/packages/core/test/acceptance/environment_injector_spec.ts @@ -88,7 +88,7 @@ describe('environment injector', () => { expect(cRef.instance.service).toBeInstanceOf(Service); }); - it('should support the ENVIRONMENT_INITIALIZER muli-token', () => { + it('should support the ENVIRONMENT_INITIALIZER multi-token', () => { let initialized = false; createEnvironmentInjector([{ provide: ENVIRONMENT_INITIALIZER, @@ -99,6 +99,20 @@ describe('environment injector', () => { expect(initialized).toBeTrue(); }); + it('should throw when the ENVIRONMENT_INITIALIZER is not a multi-token', () => { + const parentEnvInjector = TestBed.inject(EnvironmentInjector); + const providers = [{ + provide: ENVIRONMENT_INITIALIZER, + useValue: () => {}, + }]; + expect(() => createEnvironmentInjector(providers, parentEnvInjector)) + .toThrowError( + 'NG0209: Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' + + '(expected an array, but got function). ' + + 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as ' + + 'a `multi: true` provider.'); + }); + it('should adopt environment-scoped providers', () => { const injector = createEnvironmentInjector([]); const EnvScopedToken = new InjectionToken('env-scoped token', { From 676bf041c1643ee28be59ce6d4efc3e3fbab89b6 Mon Sep 17 00:00:00 2001 From: Bob Watson Date: Wed, 13 Jul 2022 15:24:35 -0700 Subject: [PATCH 05/40] docs: correct debugging task step (#46832) Also remove lint errors. Fix: #42781 PR Close #46832 --- aio/content/guide/test-debugging.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aio/content/guide/test-debugging.md b/aio/content/guide/test-debugging.md index 2d7e28a5e57e..c90786db92bb 100644 --- a/aio/content/guide/test-debugging.md +++ b/aio/content/guide/test-debugging.md @@ -7,12 +7,12 @@ Debug specs in the browser in the same way that you debug an application. 1. Reveal the Karma browser window. See [Set up testing](guide/testing#set-up-testing) if you need help with this step. -1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests. -1. Open the browser's "Developer Tools" \(`Ctrl-Shift-I` on Windows; `Command-Option-I` in macOS\). -1. Pick the "sources" section. -1. Open the `1st.spec.ts` test file \(Control/Command-P, then start typing the name of the file\). -1. Set a breakpoint in the test. -1. Refresh the browser, and it stops at the breakpoint. +1. Click the **DEBUG** button to open a new browser tab and re-run the tests. +2. Open the browser's **Developer Tools**. On Windows, press `Ctrl-Shift-I`. On macOS, press `Command-Option-I`. +3. Pick the **Sources** section. +4. Press `Control/Command-P`, and then start typing the name of your test file to open it. +5. Set a breakpoint in the test. +6. Refresh the browser, and notice how it stops at the breakpoint.