From d8c5bf89f933751245d3a6ecc27655aa198b616f Mon Sep 17 00:00:00 2001 From: Matthew Beck Date: Sun, 10 May 2026 18:45:02 -0700 Subject: [PATCH] fix(compiler): remove deprecated shadow CSS encapsulation polyfills Completely remove support for `polyfill-next-selector`, `polyfill-unscoped-rule`, and `polyfill-rule` from `shadow_css.ts`, along with their associated methods and regular expressions. Also delete `polyfills_spec.ts` entirely. --- packages/compiler/src/shadow_css.ts | 81 ------------------- .../test/shadow_css/polyfills_spec.ts | 54 ------------- 2 files changed, 135 deletions(-) delete mode 100644 packages/compiler/test/shadow_css/polyfills_spec.ts diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts index 93c174b5e6b8..6aa490ffd3db 100644 --- a/packages/compiler/src/shadow_css.ts +++ b/packages/compiler/src/shadow_css.ts @@ -192,18 +192,12 @@ export class ShadowCss { return COMMENT_PLACEHOLDER; }); - cssText = this._insertDirectives(cssText); const scopedCssText = this._scopeCssText(cssText, selector, hostSelector); // Add back comments at the original position. let commentIdx = 0; return scopedCssText.replace(_commentWithHashPlaceHolderRe, () => comments[commentIdx++]); } - private _insertDirectives(cssText: string): string { - cssText = this._insertPolyfillDirectivesInCssText(cssText); - return this._insertPolyfillRulesInCssText(cssText); - } - /** * Process styles to add scope to keyframes. * @@ -410,48 +404,6 @@ export class ShadowCss { return {...rule, content}; } - /* - * Process styles to convert native ShadowDOM rules that will trip - * up the css parser; we rely on decorating the stylesheet with inert rules. - * - * For example, we convert this rule: - * - * polyfill-next-selector { content: ':host menu-item'; } - * ::content menu-item { - * - * to this: - * - * scopeName menu-item { - * - **/ - private _insertPolyfillDirectivesInCssText(cssText: string): string { - return cssText.replace(_cssContentNextSelectorRe, function (...m: string[]) { - return m[2] + '{'; - }); - } - - /* - * Process styles to add rules which will only apply under the polyfill - * - * For example, we convert this rule: - * - * polyfill-rule { - * content: ':host menu-item'; - * ... - * } - * - * to this: - * - * scopeName menu-item {...} - * - **/ - private _insertPolyfillRulesInCssText(cssText: string): string { - return cssText.replace(_cssContentRuleRe, (...m: string[]) => { - const rule = m[0].replace(m[1], '').replace(m[2], ''); - return m[4] + rule; - }); - } - /* Ensure styles are scoped. Pseudo-scoping takes a rule like: * * .foo {... } @@ -461,7 +413,6 @@ export class ShadowCss { * scopeName .foo { ... } */ private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string { - const unscopedRules = this._extractUnscopedRulesFromCssText(cssText); // replace :host and :host-context with -shadowcsshost and -shadowcsshostcontext respectively cssText = this._insertPolyfillHostInCssText(cssText); cssText = this._convertColonHost(cssText); @@ -471,36 +422,9 @@ export class ShadowCss { cssText = this._scopeKeyframesRelatedCss(cssText, scopeSelector); cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector); } - cssText = cssText + '\n' + unscopedRules; return cssText.trim(); } - /* - * Process styles to add rules which will only apply under the polyfill - * and do not process via CSSOM. (CSSOM is destructive to rules on rare - * occasions, e.g. -webkit-calc on Safari.) - * For example, we convert this rule: - * - * @polyfill-unscoped-rule { - * content: 'menu-item'; - * ... } - * - * to this: - * - * menu-item {...} - * - **/ - private _extractUnscopedRulesFromCssText(cssText: string): string { - let r = ''; - let m: RegExpExecArray | null; - _cssContentUnscopedRuleRe.lastIndex = 0; - while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) { - const rule = m[0].replace(m[2], '').replace(m[1], m[4]); - r += rule + '\n\n'; - } - return r; - } - /* * convert a rule like :host(.foo) > .bar { } * @@ -1062,11 +986,6 @@ class SafeSelector { const _cssScopedPseudoFunctionPrefix = '(:(where|is)\\()?'; const _cssPrefixWithPseudoSelectorFunction = /:(where|is)\(/gi; -const _cssContentNextSelectorRe = - /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim; -const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim; -const _cssContentUnscopedRuleRe = - /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim; const _polyfillHost = '-shadowcsshost'; // note: :host-context pre-processed to -shadowcsshostcontext. const _polyfillHostContext = '-shadowcsscontext'; diff --git a/packages/compiler/test/shadow_css/polyfills_spec.ts b/packages/compiler/test/shadow_css/polyfills_spec.ts deleted file mode 100644 index e6ed1e58beda..000000000000 --- a/packages/compiler/test/shadow_css/polyfills_spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license - * Copyright Google LLC 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.dev/license - */ - -import {shim} from './utils'; - -describe('ShadowCss, polyfills', () => { - it('should support polyfill-next-selector', () => { - let css = shim("polyfill-next-selector {content: 'x > y'} z {}", 'contenta'); - expect(css).toEqualCss('x[contenta] > y[contenta]{}'); - - css = shim('polyfill-next-selector {content: "x > y"} z {}', 'contenta'); - expect(css).toEqualCss('x[contenta] > y[contenta]{}'); - - css = shim(`polyfill-next-selector {content: 'button[priority="1"]'} z {}`, 'contenta'); - expect(css).toEqualCss('button[priority="1"][contenta]{}'); - }); - - it('should support polyfill-unscoped-rule', () => { - let css = shim("polyfill-unscoped-rule {content: '#menu > .bar';color: blue;}", 'contenta'); - expect(css).toContain('#menu > .bar {;color: blue;}'); - - css = shim('polyfill-unscoped-rule {content: "#menu > .bar";color: blue;}', 'contenta'); - expect(css).toContain('#menu > .bar {;color: blue;}'); - - css = shim(`polyfill-unscoped-rule {content: 'button[priority="1"]'}`, 'contenta'); - expect(css).toContain('button[priority="1"] {}'); - }); - - it('should support multiple instances polyfill-unscoped-rule', () => { - const css = shim( - "polyfill-unscoped-rule {content: 'foo';color: blue;}" + - "polyfill-unscoped-rule {content: 'bar';color: blue;}", - 'contenta', - ); - expect(css).toContain('foo {;color: blue;}'); - expect(css).toContain('bar {;color: blue;}'); - }); - - it('should support polyfill-rule', () => { - let css = shim("polyfill-rule {content: ':host.foo .bar';color: blue;}", 'contenta', 'a-host'); - expect(css).toEqualCss('.foo[a-host] .bar[contenta] {;color:blue;}'); - - css = shim('polyfill-rule {content: ":host.foo .bar";color:blue;}', 'contenta', 'a-host'); - expect(css).toEqualCss('.foo[a-host] .bar[contenta] {;color:blue;}'); - - css = shim(`polyfill-rule {content: 'button[priority="1"]'}`, 'contenta', 'a-host'); - expect(css).toEqualCss('button[priority="1"][contenta] {}'); - }); -});