diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 1df80f32de60..50749762093f 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -25,7 +25,7 @@ jobs:
steps:
- name: 'Checkout code'
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@@ -47,6 +47,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
- uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
+ uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
with:
sarif_file: results.sarif
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d6c7de5d0d7..c0505f8e12dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+
+# 18.2.10 (2024-10-30)
+### compiler
+| Commit | Type | Description |
+| -- | -- | -- |
+| [69dce38e778](https://github.com/angular/angular/commit/69dce38e778cb4c15aa06347031765a84e3ac6a5) | fix | Revert: transform pseudo selectors correctly for the encapsulated view. ([#58417](https://github.com/angular/angular/pull/58417)) |
+### localize
+| Commit | Type | Description |
+| -- | -- | -- |
+| [3b989ac5bd9](https://github.com/angular/angular/commit/3b989ac5bd951a3d28bcd0ada150fc81503a016a) | fix | Adding arb format to the list of valid formats in the localization extractor cli ([#58287](https://github.com/angular/angular/pull/58287)) |
+
+
+
# 18.2.9 (2024-10-23)
### compiler-cli
diff --git a/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts b/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts
index a3eb7ca0958d..4de7ca7fdd9d 100644
--- a/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts
+++ b/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts
@@ -1,6 +1,6 @@
// #docplaster
// #docregion form-builder
-import {Component} from '@angular/core';
+import {Component, inject} from '@angular/core';
// #docregion form-builder-imports
import {FormBuilder} from '@angular/forms';
// #enddocregion form-builder-imports, form-builder
@@ -15,6 +15,9 @@ import {FormArray} from '@angular/forms';
styleUrls: ['./profile-editor.component.css'],
})
export class ProfileEditorComponent {
+ // #docregion inject-form-builder
+ private formBuilder = inject(FormBuilder);
+ // #enddocregion inject-form-builder
// #docregion formgroup-compare
profileForm = this.formBuilder.group({
firstName: [''],
@@ -34,11 +37,6 @@ export class ProfileEditorComponent {
return this.profileForm.get('aliases') as FormArray;
}
- // #docregion inject-form-builder, form-builder
-
- constructor(private formBuilder: FormBuilder) {}
- // #enddocregion inject-form-builder, form-builder
-
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
diff --git a/adev/src/content/guide/components/lifecycle.md b/adev/src/content/guide/components/lifecycle.md
index 1caac7bc72b4..e95fc7b9106d 100644
--- a/adev/src/content/guide/components/lifecycle.md
+++ b/adev/src/content/guide/components/lifecycle.md
@@ -175,8 +175,8 @@ During initialization, the first `ngDoCheck` runs after `ngOnInit`.
### ngAfterContentInit
-The `ngAfterContentInit` method runs once after all the children nested inside the component (
-its _content_) have been initialized.
+The `ngAfterContentInit` method runs once after all the children nested inside the component (its
+_content_) have been initialized.
You can use this lifecycle hook to read the results of
[content queries](guide/components/queries#content-queries). While you can access the initialized
diff --git a/adev/src/content/guide/forms/reactive-forms.md b/adev/src/content/guide/forms/reactive-forms.md
index dd830ffe3bd4..6452e3235d7d 100644
--- a/adev/src/content/guide/forms/reactive-forms.md
+++ b/adev/src/content/guide/forms/reactive-forms.md
@@ -259,7 +259,7 @@ Import the `FormBuilder` class from the `@angular/forms` package.
-The `FormBuilder` service is an injectable provider that is provided with the reactive forms module. Inject this dependency by adding it to the component constructor.
+The `FormBuilder` service is an injectable provider from the reactive forms module. Use the `inject()` function to inject this dependency in your component.
diff --git a/adev/src/content/guide/templates/expression-syntax.md b/adev/src/content/guide/templates/expression-syntax.md
index 74e75c96716f..0091a4ca735a 100644
--- a/adev/src/content/guide/templates/expression-syntax.md
+++ b/adev/src/content/guide/templates/expression-syntax.md
@@ -19,10 +19,10 @@ Angular supports a subset of [literal values](https://developer.mozilla.org/en-U
### Unsupported literals
-| Literal type | Example value |
-| --------------- | ------------------- |
-| Template string | `\`Hello ${name}\`` |
-| RegExp | `/\d+/` |
+| Literal type | Example value |
+| --------------- | --------------------- |
+| Template string | `` `Hello ${name}` `` |
+| RegExp | `/\d+/` |
## Globals
diff --git a/package.json b/package.json
index 35388d3313cf..773374cd35ce 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "angular-srcs",
- "version": "18.2.9",
+ "version": "18.2.10",
"private": true,
"description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",
diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts
index bbaad8b43cf4..f21dc0027022 100644
--- a/packages/compiler/src/shadow_css.ts
+++ b/packages/compiler/src/shadow_css.ts
@@ -338,7 +338,7 @@ export class ShadowCss {
* captures how many (if any) leading whitespaces are present or a comma
* - (?:(?:(['"])((?:\\\\|\\\2|(?!\2).)+)\2)|(-?[A-Za-z][\w\-]*))
* captures two different possible keyframes, ones which are quoted or ones which are valid css
- * indents (custom properties excluded)
+ * idents (custom properties excluded)
* - (?=[,\s;]|$)
* simply matches the end of the possible keyframe, valid endings are: a comma, a space, a
* semicolon or the end of the string
@@ -459,7 +459,7 @@ export class ShadowCss {
*/
private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string {
const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
- // replace :host and :host-context with -shadowcsshost and -shadowcsshostcontext respectively
+ // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
cssText = this._insertPolyfillHostInCssText(cssText);
cssText = this._convertColonHost(cssText);
cssText = this._convertColonHostContext(cssText);
@@ -539,7 +539,7 @@ export class ShadowCss {
* .foo .bar { ... }
*/
private _convertColonHostContext(cssText: string): string {
- return cssText.replace(_cssColonHostContextReGlobal, (selectorText, pseudoPrefix) => {
+ return cssText.replace(_cssColonHostContextReGlobal, (selectorText) => {
// We have captured a selector that contains a `:host-context` rule.
// For backward compatibility `:host-context` may contain a comma separated list of selectors.
@@ -594,12 +594,10 @@ export class ShadowCss {
}
// The context selectors now must be combined with each other to capture all the possible
- // selectors that `:host-context` can match. See `_combineHostContextSelectors()` for more
+ // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more
// info about how this is done.
return contextSelectorGroups
- .map((contextSelectors) =>
- _combineHostContextSelectors(contextSelectors, selectorText, pseudoPrefix),
- )
+ .map((contextSelectors) => combineHostContextSelectors(contextSelectors, selectorText))
.join(', ');
});
}
@@ -618,12 +616,7 @@ export class ShadowCss {
let selector = rule.selector;
let content = rule.content;
if (rule.selector[0] !== '@') {
- selector = this._scopeSelector({
- selector,
- scopeSelector,
- hostSelector,
- isParentSelector: true,
- });
+ selector = this._scopeSelector(rule.selector, scopeSelector, hostSelector);
} else if (scopedAtRuleIdentifiers.some((atRule) => rule.selector.startsWith(atRule))) {
content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
} else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) {
@@ -663,44 +656,15 @@ export class ShadowCss {
});
}
- private _safeSelector: SafeSelector | undefined;
- private _shouldScopeIndicator: boolean | undefined;
-
- // `isParentSelector` is used to distinguish the selectors which are coming from
- // the initial selector string and any nested selectors, parsed recursively,
- // for example `selector = 'a:where(.one)'` could be the parent, while recursive call
- // would have `selector = '.one'`.
- private _scopeSelector({
- selector,
- scopeSelector,
- hostSelector,
- isParentSelector = false,
- }: {
- selector: string;
- scopeSelector: string;
- hostSelector: string;
- isParentSelector?: boolean;
- }): string {
- // Split the selector into independent parts by `,` (comma) unless
- // comma is within parenthesis, for example `:is(.one, two)`.
- // Negative lookup after comma allows not splitting inside nested parenthesis,
- // up to three levels (((,))).
- const selectorSplitRe =
- / ?,(?!(?:[^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\))) ?/;
-
+ private _scopeSelector(selector: string, scopeSelector: string, hostSelector: string): string {
return selector
- .split(selectorSplitRe)
+ .split(/ ?, ?/)
.map((part) => part.split(_shadowDeepSelectors))
.map((deepParts) => {
const [shallowPart, ...otherParts] = deepParts;
const applyScope = (shallowPart: string) => {
if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
- return this._applySelectorScope({
- selector: shallowPart,
- scopeSelector,
- hostSelector,
- isParentSelector,
- });
+ return this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
} else {
return shallowPart;
}
@@ -733,9 +697,9 @@ export class ShadowCss {
if (_polyfillHostRe.test(selector)) {
const replaceBy = `[${hostSelector}]`;
return selector
- .replace(_polyfillHostNoCombinatorReGlobal, (_hnc, selector) => {
+ .replace(_polyfillHostNoCombinatorRe, (hnc, selector) => {
return selector.replace(
- /([^:\)]*)(:*)(.*)/,
+ /([^:]*)(:*)(.*)/,
(_: string, before: string, colon: string, after: string) => {
return before + replaceBy + colon + after;
},
@@ -749,17 +713,11 @@ export class ShadowCss {
// return a selector with [name] suffix on each simple selector
// e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
- private _applySelectorScope({
- selector,
- scopeSelector,
- hostSelector,
- isParentSelector,
- }: {
- selector: string;
- scopeSelector: string;
- hostSelector: string;
- isParentSelector?: boolean;
- }): string {
+ private _applySelectorScope(
+ selector: string,
+ scopeSelector: string,
+ hostSelector: string,
+ ): string {
const isRe = /\[is=([^\]]*)\]/g;
scopeSelector = scopeSelector.replace(isRe, (_: string, ...parts: string[]) => parts[0]);
@@ -774,10 +732,6 @@ export class ShadowCss {
if (p.includes(_polyfillHostNoCombinator)) {
scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
- if (_polyfillHostNoCombinatorWithinPseudoFunction.test(p)) {
- const [_, before, colon, after] = scopedP.match(/([^:]*)(:*)(.*)/)!;
- scopedP = before + attrName + colon + after;
- }
} else {
// remove :host since it should be unnecessary
const t = p.replace(_polyfillHostRe, '');
@@ -792,60 +746,13 @@ export class ShadowCss {
return scopedP;
};
- // Wraps `_scopeSelectorPart()` to not use it directly on selectors with
- // pseudo selector functions like `:where()`. Selectors within pseudo selector
- // functions are recursively sent to `_scopeSelector()`.
- const _pseudoFunctionAwareScopeSelectorPart = (selectorPart: string) => {
- let scopedPart = '';
-
- const cssPrefixWithPseudoSelectorFunctionMatch = selectorPart.match(
- _cssPrefixWithPseudoSelectorFunction,
- );
- if (cssPrefixWithPseudoSelectorFunctionMatch) {
- const [cssPseudoSelectorFunction] = cssPrefixWithPseudoSelectorFunctionMatch;
-
- // Unwrap the pseudo selector to scope its contents.
- // For example,
- // - `:where(selectorToScope)` -> `selectorToScope`;
- // - `:is(.foo, .bar)` -> `.foo, .bar`.
- const selectorToScope = selectorPart.slice(cssPseudoSelectorFunction.length, -1);
-
- if (selectorToScope.includes(_polyfillHostNoCombinator)) {
- this._shouldScopeIndicator = true;
- }
-
- const scopedInnerPart = this._scopeSelector({
- selector: selectorToScope,
- scopeSelector,
- hostSelector,
- });
-
- // Put the result back into the pseudo selector function.
- scopedPart = `${cssPseudoSelectorFunction}${scopedInnerPart})`;
- } else {
- this._shouldScopeIndicator =
- this._shouldScopeIndicator || selectorPart.includes(_polyfillHostNoCombinator);
- scopedPart = this._shouldScopeIndicator ? _scopeSelectorPart(selectorPart) : selectorPart;
- }
-
- return scopedPart;
- };
-
- if (isParentSelector) {
- this._safeSelector = new SafeSelector(selector);
- selector = this._safeSelector.content();
- }
+ const safeContent = new SafeSelector(selector);
+ selector = safeContent.content();
let scopedSelector = '';
let startIndex = 0;
let res: RegExpExecArray | null;
- // Combinators aren't used as a delimiter if they are within parenthesis,
- // for example `:where(.one .two)` stays intact.
- // Similarly to selector separation by comma initially, negative lookahead
- // is used here to not break selectors within nested parenthesis up to three
- // nested layers.
- const sep =
- /( |>|\+|~(?!=))(?!([^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\)))\s*/g;
+ const sep = /( |>|\+|~(?!=))\s*/g;
// If a selector appears before :host it should not be shimmed as it
// matches on ancestor elements and not on elements in the host's shadow
@@ -859,13 +766,8 @@ export class ShadowCss {
// - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
// `:host-context(tag)`)
const hasHost = selector.includes(_polyfillHostNoCombinator);
- // Only scope parts after or on the same level as the first `-shadowcsshost-no-combinator`
- // when it is present. The selector has the same level when it is a part of a pseudo
- // selector, like `:where()`, for example `:where(:host, .foo)` would result in `.foo`
- // being scoped.
- if (isParentSelector || this._shouldScopeIndicator) {
- this._shouldScopeIndicator = !hasHost;
- }
+ // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
+ let shouldScope = !hasHost;
while ((res = sep.exec(selector)) !== null) {
const separator = res[1];
@@ -884,17 +786,18 @@ export class ShadowCss {
continue;
}
- const scopedPart = _pseudoFunctionAwareScopeSelectorPart(part);
+ shouldScope = shouldScope || part.includes(_polyfillHostNoCombinator);
+ const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
scopedSelector += `${scopedPart} ${separator} `;
startIndex = sep.lastIndex;
}
const part = selector.substring(startIndex);
- scopedSelector += _pseudoFunctionAwareScopeSelectorPart(part);
+ shouldScope = shouldScope || part.includes(_polyfillHostNoCombinator);
+ scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
// replace the placeholders with their original values
- // using values stored inside the `safeSelector` instance.
- return this._safeSelector!.restore(scopedSelector);
+ return safeContent.restore(scopedSelector);
}
private _insertPolyfillHostInCssText(selector: string): string {
@@ -959,8 +862,6 @@ class SafeSelector {
}
}
-const _cssScopedPseudoFunctionPrefix = '(:(where|is)\\()?';
-const _cssPrefixWithPseudoSelectorFunction = /^:(where|is)\(/i;
const _cssContentNextSelectorRe =
/polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
@@ -971,17 +872,10 @@ const _polyfillHost = '-shadowcsshost';
const _polyfillHostContext = '-shadowcsscontext';
const _parenSuffix = '(?:\\((' + '(?:\\([^)(]*\\)|[^)(]*)+?' + ')\\))?([^,{]*)';
const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix, 'gim');
-const _cssColonHostContextReGlobal = new RegExp(
- _cssScopedPseudoFunctionPrefix + '(' + _polyfillHostContext + _parenSuffix + ')',
- 'gim',
-);
+const _cssColonHostContextReGlobal = new RegExp(_polyfillHostContext + _parenSuffix, 'gim');
const _cssColonHostContextRe = new RegExp(_polyfillHostContext + _parenSuffix, 'im');
const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
-const _polyfillHostNoCombinatorWithinPseudoFunction = new RegExp(
- `:.*\\(.*${_polyfillHostNoCombinator}.*\\)`,
-);
const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
-const _polyfillHostNoCombinatorReGlobal = new RegExp(_polyfillHostNoCombinatorRe, 'g');
const _shadowDOMSelectorsRe = [
/::shadow/g,
/::content/g,
@@ -1232,11 +1126,7 @@ function unescapeQuotes(str: string, isQuoted: boolean): string {
* @param contextSelectors an array of context selectors that will be combined.
* @param otherSelectors the rest of the selectors that are not context selectors.
*/
-function _combineHostContextSelectors(
- contextSelectors: string[],
- otherSelectors: string,
- pseudoPrefix = '',
-): string {
+function combineHostContextSelectors(contextSelectors: string[], otherSelectors: string): string {
const hostMarker = _polyfillHostNoCombinator;
_polyfillHostRe.lastIndex = 0; // reset the regex to ensure we get an accurate test
const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
@@ -1265,8 +1155,8 @@ function _combineHostContextSelectors(
return combined
.map((s) =>
otherSelectorsHasHost
- ? `${pseudoPrefix}${s}${otherSelectors}`
- : `${pseudoPrefix}${s}${hostMarker}${otherSelectors}, ${pseudoPrefix}${s} ${hostMarker}${otherSelectors}`,
+ ? `${s}${otherSelectors}`
+ : `${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`,
)
.join(',');
}
diff --git a/packages/compiler/test/shadow_css/host_and_host_context_spec.ts b/packages/compiler/test/shadow_css/host_and_host_context_spec.ts
index 5b5689feb2be..c4dc29c372b4 100644
--- a/packages/compiler/test/shadow_css/host_and_host_context_spec.ts
+++ b/packages/compiler/test/shadow_css/host_and_host_context_spec.ts
@@ -107,42 +107,6 @@ describe('ShadowCss, :host and :host-context', () => {
});
describe(':host-context', () => {
- it('should transform :host-context with pseudo selectors', () => {
- expect(
- shim(':host-context(backdrop:not(.borderless)) .backdrop {}', 'contenta', 'hosta'),
- ).toEqualCss(
- 'backdrop:not(.borderless)[hosta] .backdrop[contenta], backdrop:not(.borderless) [hosta] .backdrop[contenta] {}',
- );
- expect(shim(':where(:host-context(backdrop)) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(backdrop[hosta]), :where(backdrop [hosta]) {}',
- );
- expect(shim(':where(:host-context(outer1)) :host(bar) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(outer1) bar[hosta] {}',
- );
- expect(
- shim(':where(:host-context(.one)) :where(:host-context(.two)) {}', 'contenta', 'a-host'),
- ).toEqualCss(
- ':where(.one.two[a-host]), ' + // `one` and `two` both on the host
- ':where(.one.two [a-host]), ' + // `one` and `two` are both on the same ancestor
- ':where(.one .two[a-host]), ' + // `one` is an ancestor and `two` is on the host
- ':where(.one .two [a-host]), ' + // `one` and `two` are both ancestors (in that order)
- ':where(.two .one[a-host]), ' + // `two` is an ancestor and `one` is on the host
- ':where(.two .one [a-host])' + // `two` and `one` are both ancestors (in that order)
- ' {}',
- );
- expect(
- shim(':where(:host-context(backdrop)) .foo ~ .bar {}', 'contenta', 'hosta'),
- ).toEqualCss(
- ':where(backdrop[hosta]) .foo[contenta] ~ .bar[contenta], :where(backdrop [hosta]) .foo[contenta] ~ .bar[contenta] {}',
- );
- expect(shim(':where(:host-context(backdrop)) :host {}', 'contenta', 'hosta')).toEqualCss(
- ':where(backdrop) [hosta] {}',
- );
- expect(shim('div:where(:host-context(backdrop)) :host {}', 'contenta', 'hosta')).toEqualCss(
- 'div:where(backdrop) [hosta] {}',
- );
- });
-
it('should handle tag selector', () => {
expect(shim(':host-context(div) {}', 'contenta', 'a-host')).toEqualCss(
'div[a-host], div [a-host] {}',
diff --git a/packages/compiler/test/shadow_css/shadow_css_spec.ts b/packages/compiler/test/shadow_css/shadow_css_spec.ts
index 77a0a361a319..e7e038d1b52e 100644
--- a/packages/compiler/test/shadow_css/shadow_css_spec.ts
+++ b/packages/compiler/test/shadow_css/shadow_css_spec.ts
@@ -66,18 +66,6 @@ describe('ShadowCss', () => {
expect(shim('one[attr="va lue"] {}', 'contenta')).toEqualCss('one[attr="va lue"][contenta] {}');
expect(shim('one[attr] {}', 'contenta')).toEqualCss('one[attr][contenta] {}');
expect(shim('[is="one"] {}', 'contenta')).toEqualCss('[is="one"][contenta] {}');
- expect(shim('[attr] {}', 'contenta')).toEqualCss('[attr][contenta] {}');
- });
-
- it('should transform :host with attributes', () => {
- expect(shim(':host [attr] {}', 'contenta', 'hosta')).toEqualCss('[hosta] [attr][contenta] {}');
- expect(shim(':host(create-first-project) {}', 'contenta', 'hosta')).toEqualCss(
- 'create-first-project[hosta] {}',
- );
- expect(shim(':host[attr] {}', 'contenta', 'hosta')).toEqualCss('[attr][hosta] {}');
- expect(shim(':host[attr]:where(:not(.cm-button)) {}', 'contenta', 'hosta')).toEqualCss(
- '[attr][hosta]:where(:not(.cm-button)) {}',
- );
});
it('should handle escaped sequences in selectors', () => {
@@ -89,171 +77,6 @@ describe('ShadowCss', () => {
expect(shim('.one\\:two .three\\:four {}', 'contenta')).toEqualCss(
'.one\\:two[contenta] .three\\:four[contenta] {}',
);
- expect(shim('div:where(.one) {}', 'contenta', 'hosta')).toEqualCss(
- 'div[contenta]:where(.one) {}',
- );
- expect(shim('div:where() {}', 'contenta', 'hosta')).toEqualCss('div[contenta]:where() {}');
- // See `xit('should parse concatenated pseudo selectors'`
- expect(shim(':where(a):where(b) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(a)[contenta]:where(b) {}',
- );
- expect(shim('*:where(.one) {}', 'contenta', 'hosta')).toEqualCss('*[contenta]:where(.one) {}');
- expect(shim('*:where(.one) ::ng-deep .foo {}', 'contenta', 'hosta')).toEqualCss(
- '*[contenta]:where(.one) .foo {}',
- );
- });
-
- xit('should parse concatenated pseudo selectors', () => {
- // Current logic leads to a result with an outer scope
- // It could be changed, to not increase specificity
- // Requires a more complex parsing
- expect(shim(':where(a):where(b) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(a[contenta]):where(b[contenta]) {}',
- );
- });
-
- it('should handle pseudo functions correctly', () => {
- // :where()
- expect(shim(':where(.one) {}', 'contenta', 'hosta')).toEqualCss(':where(.one[contenta]) {}');
- expect(shim(':where(div.one span.two) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(div.one[contenta] span.two[contenta]) {}',
- );
- expect(shim(':where(.one) .two {}', 'contenta', 'hosta')).toEqualCss(
- ':where(.one[contenta]) .two[contenta] {}',
- );
- expect(shim(':where(:host) {}', 'contenta', 'hosta')).toEqualCss(':where([hosta]) {}');
- expect(shim(':where(:host) .one {}', 'contenta', 'hosta')).toEqualCss(
- ':where([hosta]) .one[contenta] {}',
- );
- expect(shim(':where(.one) :where(:host) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(.one) :where([hosta]) {}',
- );
- expect(shim(':where(.one :host) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(.one [hosta]) {}',
- );
- expect(shim('div :where(.one) {}', 'contenta', 'hosta')).toEqualCss(
- 'div[contenta] :where(.one[contenta]) {}',
- );
- expect(shim(':host :where(.one .two) {}', 'contenta', 'hosta')).toEqualCss(
- '[hosta] :where(.one[contenta] .two[contenta]) {}',
- );
- expect(shim(':where(.one, .two) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(.one[contenta], .two[contenta]) {}',
- );
- expect(shim(':where(.one > .two) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(.one[contenta] > .two[contenta]) {}',
- );
- expect(shim(':where(> .one) {}', 'contenta', 'hosta')).toEqualCss(
- ':where( > .one[contenta]) {}',
- );
- expect(shim(':where(:not(.one) ~ .two) {}', 'contenta', 'hosta')).toEqualCss(
- ':where([contenta]:not(.one) ~ .two[contenta]) {}',
- );
- expect(shim(':where([foo]) {}', 'contenta', 'hosta')).toEqualCss(':where([foo][contenta]) {}');
-
- // :is()
- expect(shim('div:is(.foo) {}', 'contenta', 'a-host')).toEqualCss('div[contenta]:is(.foo) {}');
- expect(shim(':is(.dark :host) {}', 'contenta', 'a-host')).toEqualCss(':is(.dark [a-host]) {}');
- expect(shim(':is(.dark) :is(:host) {}', 'contenta', 'a-host')).toEqualCss(
- ':is(.dark) :is([a-host]) {}',
- );
- expect(shim(':host:is(.foo) {}', 'contenta', 'a-host')).toEqualCss('[a-host]:is(.foo) {}');
- expect(shim(':is(.foo) {}', 'contenta', 'a-host')).toEqualCss(':is(.foo[contenta]) {}');
- expect(shim(':is(.foo, .bar, .baz) {}', 'contenta', 'a-host')).toEqualCss(
- ':is(.foo[contenta], .bar[contenta], .baz[contenta]) {}',
- );
- expect(shim(':is(.foo, .bar) :host {}', 'contenta', 'a-host')).toEqualCss(
- ':is(.foo, .bar) [a-host] {}',
- );
-
- // :is() and :where()
- expect(
- shim(
- ':is(.foo, .bar) :is(.baz) :where(.one, .two) :host :where(.three:first-child) {}',
- 'contenta',
- 'a-host',
- ),
- ).toEqualCss(
- ':is(.foo, .bar) :is(.baz) :where(.one, .two) [a-host] :where(.three[contenta]:first-child) {}',
- );
- expect(shim(':where(:is(a)) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(:is(a[contenta])) {}',
- );
- expect(shim(':where(:is(a, b)) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(:is(a[contenta], b[contenta])) {}',
- );
- expect(shim(':where(:host:is(.one, .two)) {}', 'contenta', 'hosta')).toEqualCss(
- ':where([hosta]:is(.one, .two)) {}',
- );
- expect(shim(':where(:host :is(.one, .two)) {}', 'contenta', 'hosta')).toEqualCss(
- ':where([hosta] :is(.one[contenta], .two[contenta])) {}',
- );
- expect(shim(':where(:is(a, b) :is(.one, .two)) {}', 'contenta', 'hosta')).toEqualCss(
- ':where(:is(a[contenta], b[contenta]) :is(.one[contenta], .two[contenta])) {}',
- );
- expect(
- shim(
- ':where(:where(a:has(.foo), b) :is(.one, .two:where(.foo > .bar))) {}',
- 'contenta',
- 'hosta',
- ),
- ).toEqualCss(
- ':where(:where(a[contenta]:has(.foo), b[contenta]) :is(.one[contenta], .two[contenta]:where(.foo > .bar))) {}',
- );
-
- // complex selectors
- expect(shim(':host:is([foo],[foo-2])>div.example-2 {}', 'contenta', 'a-host')).toEqualCss(
- '[a-host]:is([foo],[foo-2]) > div.example-2[contenta] {}',
- );
- expect(shim(':host:is([foo], [foo-2]) > div.example-2 {}', 'contenta', 'a-host')).toEqualCss(
- '[a-host]:is([foo], [foo-2]) > div.example-2[contenta] {}',
- );
- expect(shim(':host:has([foo],[foo-2])>div.example-2 {}', 'contenta', 'a-host')).toEqualCss(
- '[a-host]:has([foo],[foo-2]) > div.example-2[contenta] {}',
- );
-
- // :has()
- expect(shim('div:has(a) {}', 'contenta', 'hosta')).toEqualCss('div[contenta]:has(a) {}');
- expect(shim('div:has(a) :host {}', 'contenta', 'hosta')).toEqualCss('div:has(a) [hosta] {}');
- expect(shim(':has(a) :host :has(b) {}', 'contenta', 'hosta')).toEqualCss(
- ':has(a) [hosta] [contenta]:has(b) {}',
- );
- expect(shim('div:has(~ .one) {}', 'contenta', 'hosta')).toEqualCss(
- 'div[contenta]:has(~ .one) {}',
- );
- // Unlike `:is()` or `:where()` the attribute selector isn't placed inside
- // of `:has()`. That is deliberate, `[contenta]:has(a)` would select all
- // `[contenta]` with `a` inside, while `:has(a[contenta])` would select
- // everything that contains `a[contenta]`, targeting elements outside of
- // encapsulated scope.
- expect(shim(':has(a) :has(b) {}', 'contenta', 'hosta')).toEqualCss(
- '[contenta]:has(a) [contenta]:has(b) {}',
- );
- });
-
- it('should handle :host inclusions inside pseudo-selectors selectors', () => {
- expect(shim('.header:not(.admin) {}', 'contenta', 'hosta')).toEqualCss(
- '.header[contenta]:not(.admin) {}',
- );
- expect(shim('.header:is(:host > .toolbar, :host ~ .panel) {}', 'contenta', 'hosta')).toEqualCss(
- '.header[contenta]:is([hosta] > .toolbar, [hosta] ~ .panel) {}',
- );
- expect(
- shim('.header:where(:host > .toolbar, :host ~ .panel) {}', 'contenta', 'hosta'),
- ).toEqualCss('.header[contenta]:where([hosta] > .toolbar, [hosta] ~ .panel) {}');
- expect(shim('.header:not(.admin, :host.super .header) {}', 'contenta', 'hosta')).toEqualCss(
- '.header[contenta]:not(.admin, .super[hosta] .header) {}',
- );
- expect(
- shim('.header:not(.admin, :host.super .header, :host.mega .header) {}', 'contenta', 'hosta'),
- ).toEqualCss('.header[contenta]:not(.admin, .super[hosta] .header, .mega[hosta] .header) {}');
-
- expect(shim('.one :where(.two, :host) {}', 'contenta', 'hosta')).toEqualCss(
- '.one :where(.two[contenta], [hosta]) {}',
- );
- expect(shim('.one :where(:host, .two) {}', 'contenta', 'hosta')).toEqualCss(
- '.one :where([hosta], .two[contenta]) {}',
- );
});
it('should handle escaped selector with space (if followed by a hex char)', () => {
diff --git a/packages/core/src/render3/util/change_detection_utils.ts b/packages/core/src/render3/util/change_detection_utils.ts
index 407c4666ba04..8a92a746be7c 100644
--- a/packages/core/src/render3/util/change_detection_utils.ts
+++ b/packages/core/src/render3/util/change_detection_utils.ts
@@ -22,7 +22,6 @@ import {getRootComponents} from './discovery_utils';
* @param component Component to {@link ChangeDetectorRef#markForCheck mark for check}.
*
* @publicApi
- * @globalApi ng
*/
export function applyChanges(component: {}): void {
ngDevMode && assertDefined(component, 'component');
diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts
index f0bb7eb1bf98..ee9bd487da85 100644
--- a/packages/core/src/render3/util/discovery_utils.ts
+++ b/packages/core/src/render3/util/discovery_utils.ts
@@ -51,7 +51,6 @@ import {getLViewParent, unwrapRNode} from './view_utils';
* is no component associated with it.
*
* @publicApi
- * @globalApi ng
*/
export function getComponent(element: Element): T | null {
ngDevMode && assertDomElement(element);
@@ -79,7 +78,6 @@ export function getComponent(element: Element): T | null {
* inside any component.
*
* @publicApi
- * @globalApi ng
*/
export function getContext(element: Element): T | null {
assertDomElement(element);
@@ -101,7 +99,6 @@ export function getContext(element: Element): T | null {
* part of a component view.
*
* @publicApi
- * @globalApi ng
*/
export function getOwningComponent(elementOrDir: Element | {}): T | null {
const context = getLContext(elementOrDir)!;
@@ -124,7 +121,6 @@ export function getOwningComponent(elementOrDir: Element | {}): T | null {
* @returns Root components associated with the target object.
*
* @publicApi
- * @globalApi ng
*/
export function getRootComponents(elementOrDir: Element | {}): {}[] {
const lView = readPatchedLView<{}>(elementOrDir);
@@ -139,7 +135,6 @@ export function getRootComponents(elementOrDir: Element | {}): {}[] {
* @returns Injector associated with the element, component or directive instance.
*
* @publicApi
- * @globalApi ng
*/
export function getInjector(elementOrDir: Element | {}): Injector {
const context = getLContext(elementOrDir)!;
@@ -201,7 +196,6 @@ export function getInjectionTokens(element: Element): any[] {
* @returns Array of directives associated with the node.
*
* @publicApi
- * @globalApi ng
*/
export function getDirectives(node: Node): {}[] {
// Skip text nodes because we can't have directives associated with them.
@@ -266,7 +260,6 @@ export interface ComponentDebugMetadata extends DirectiveDebugMetadata {
* @returns metadata of the passed directive or component
*
* @publicApi
- * @globalApi ng
*/
export function getDirectiveMetadata(
directiveOrComponentInstance: any,
@@ -329,7 +322,6 @@ export function getLocalRefs(target: {}): {[key: string]: any} {
* @returns Host element of the target.
*
* @publicApi
- * @globalApi ng
*/
export function getHostElement(componentOrDirective: {}): Element {
return getLContext(componentOrDirective)!.native as unknown as Element;
@@ -398,7 +390,6 @@ export interface Listener {
* @returns Array of event listeners on the DOM element.
*
* @publicApi
- * @globalApi ng
*/
export function getListeners(element: Element): Listener[] {
ngDevMode && assertDomElement(element);
diff --git a/packages/localize/src/localize/src/localize.ts b/packages/localize/src/localize/src/localize.ts
index 8c478435df76..e862d91a7f90 100644
--- a/packages/localize/src/localize/src/localize.ts
+++ b/packages/localize/src/localize/src/localize.ts
@@ -141,7 +141,6 @@ export interface TranslateFn {
* @param expressions a collection of the values of each placeholder in the template string.
* @returns the translated string, with the `messageParts` and `expressions` interleaved together.
*
- * @globalApi
* @publicApi
*/
export const $localize: LocalizeFn = function (
diff --git a/packages/localize/tools/src/extract/cli.ts b/packages/localize/tools/src/extract/cli.ts
index 391b30831dbc..7ec28c74a8da 100644
--- a/packages/localize/tools/src/extract/cli.ts
+++ b/packages/localize/tools/src/extract/cli.ts
@@ -48,7 +48,18 @@ const options = yargs(args)
.option('f', {
alias: 'format',
required: true,
- choices: ['xmb', 'xlf', 'xlif', 'xliff', 'xlf2', 'xlif2', 'xliff2', 'json', 'legacy-migrate'],
+ choices: [
+ 'xmb',
+ 'xlf',
+ 'xlif',
+ 'xliff',
+ 'xlf2',
+ 'xlif2',
+ 'xliff2',
+ 'json',
+ 'legacy-migrate',
+ 'arb',
+ ],
describe: 'The format of the translation file.',
type: 'string',
})
diff --git a/packages/router/src/models.ts b/packages/router/src/models.ts
index e116bbdcdcde..ba11476559d8 100644
--- a/packages/router/src/models.ts
+++ b/packages/router/src/models.ts
@@ -1115,6 +1115,9 @@ export interface CanMatch {
*
* {@example router/route_functional_guards.ts region="CanMatchFn"}
*
+ * @param route The route configuration.
+ * @param segments The URL segments that have not been consumed by previous parent route evaluations.
+ *
* @publicApi
* @see {@link Route}
*/