From 1f93af99ad04fdd1eedb7234aad4924dbb8d9783 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Wed, 17 May 2023 13:14:18 +0000 Subject: [PATCH] fix(compiler): do not remove comments in component styles Prior to this commit, comments in CSS were being removed. This caused inline sourcemaps to break to the shift in lines. This caused sourcemaps to break in the ESBuild based builder as this always adds comments at the top of the file with the filename. Example ```css /* src/app/app.component.scss */ * { color: red; background: transparent; } /*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8uL3NyYy9hcHAvYXBwLmNvbXBvbmVudC5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBOzs7Ozs7Ozs7Q0FBQTtBQVdBO0VBQ0UsVUFBQTtFQUNBLHVCQUFBO0FBREYiLCJzb3VyY2VzQ29udGVudCI6WyIvL01FRElBIFFVRVJZIE1BTkFHRVJcbi8qXG4gIDAgLSA2MDA6IFBob25lXG4gIDYwMCAtIDkwMDogVGFibGV0IHBvcnRyYWl0XG4gIDkwMCAtIDEyMDA6IFRhYmxldCBsYW5kc2NhcGVcbiAgMTIwMCAtIDE4MDA6IE5vcm1hbCBzdHlsZXNcbiAgMTgwMCsgOiBCaWcgRGVza3RvcFxuICAxZW0gPSAxNnB4XG4gIFRoZSBzbWFsbGVyIGRldmljZSBydWxlcyBhbHdheXMgc2hvdWxkIHdyaXRlIGJlbG93IHRoZSBiaWdnZXIgZGV2aWNlIHJ1bGVzXG4gIEZpeGluZyBPcmRlciA9PiBCYXNlICsgVHlwb2dyYXBoeSA+PiBHZW5lcmFsIExheW91dCArIEdyaWQgPj4gUGFnZSBMYXlvdXQgKyBDb21wb25lbnRcbiovXG5cbioge1xuICBjb2xvcjogcmVkO1xuICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= */ ``` Closes #50308 --- packages/compiler/src/shadow_css.ts | 25 ++++----- .../test/shadow_css/shadow_css_spec.ts | 53 ++++++++++--------- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts index f9e75a1a9603f3..234c2aab653c0e 100644 --- a/packages/compiler/src/shadow_css.ts +++ b/packages/compiler/src/shadow_css.ts @@ -138,12 +138,12 @@ export class ShadowCss { * The hostSelector is the attribute added to the host itself. */ shimCssText(cssText: string, selector: string, hostSelector: string = ''): string { - const commentsWithHash = extractCommentsWithHash(cssText); - cssText = stripComments(cssText); + // **NOTE**: Do not strip comments as this will cause component sourcemaps to break + // due to shift in lines. cssText = this._insertDirectives(cssText); - const scopedCssText = this._scopeCssText(cssText, selector, hostSelector); - return [scopedCssText, ...commentsWithHash].join('\n'); + + return scopedCssText; } private _insertDirectives(cssText: string): string { @@ -802,22 +802,11 @@ const _polyfillHostRe = /-shadowcsshost/gim; const _colonHostRe = /:host/gim; const _colonHostContextRe = /:host-context/gim; -const _commentRe = /\/\*[\s\S]*?\*\//g; - const _placeholderRe = /__ph-(\d+)__/g; -function stripComments(input: string): string { - return input.replace(_commentRe, ''); -} - -const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g; - -function extractCommentsWithHash(input: string): string[] { - return input.match(_commentWithHashRe) || []; -} const BLOCK_PLACEHOLDER = '%BLOCK%'; -const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g; +const _ruleRe = /(\s*(?:\/\*[\s\S]*?\*\/\s*)?)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g; const CONTENT_PAIRS = new Map([['{', '}']]); const COMMA_IN_PLACEHOLDER = '%COMMA_IN_PLACEHOLDER%'; @@ -836,6 +825,7 @@ export function processRules(input: string, ruleCallback: (rule: CssRule) => Css const escaped = escapeInStrings(input); const inputWithEscapedBlocks = escapeBlocks(escaped, CONTENT_PAIRS, BLOCK_PLACEHOLDER); let nextBlockIndex = 0; + console.log(inputWithEscapedBlocks.escapedString); const escapedResult = inputWithEscapedBlocks.escapedString.replace(_ruleRe, (...m: string[]) => { const selector = m[2]; let content = ''; @@ -869,6 +859,9 @@ function escapeBlocks( const char = input[i]; if (char === '\\') { i++; + } else if (char === '/' && input.charAt(i + 1) === '*') { + // Skip content in comments. + i = input.indexOf('*/', i + 1) + 2; } else if (char === closeChar) { openCharCount--; if (openCharCount === 0) { diff --git a/packages/compiler/test/shadow_css/shadow_css_spec.ts b/packages/compiler/test/shadow_css/shadow_css_spec.ts index bf02096562505d..d885287e51353f 100644 --- a/packages/compiler/test/shadow_css/shadow_css_spec.ts +++ b/packages/compiler/test/shadow_css/shadow_css_spec.ts @@ -96,30 +96,6 @@ describe('ShadowCss', () => { expect(css).toEqualCss('div[contenta] {height:calc(100% - 55px);}'); }); - it('should strip comments', () => { - expect(shim('/* x */b {c}', 'contenta')).toEqualCss('b[contenta] {c}'); - }); - - it('should ignore special characters in comments', () => { - expect(shim('/* {;, */b {c}', 'contenta')).toEqualCss('b[contenta] {c}'); - }); - - it('should support multiline comments', () => { - expect(shim('/* \n */b {c}', 'contenta')).toEqualCss('b[contenta] {c}'); - }); - - it('should keep sourceMappingURL comments', () => { - expect(shim('b {c}/*# sourceMappingURL=data:x */', 'contenta')) - .toEqualCss('b[contenta] {c} /*# sourceMappingURL=data:x */'); - expect(shim('b {c}/* #sourceMappingURL=data:x */', 'contenta')) - .toEqualCss('b[contenta] {c} /* #sourceMappingURL=data:x */'); - }); - - it('should keep sourceURL comments', () => { - expect(shim('/*# sourceMappingURL=data:x */b {c}/*# sourceURL=xxx */', 'contenta')) - .toEqualCss('b[contenta] {c} /*# sourceMappingURL=data:x */ /*# sourceURL=xxx */'); - }); - it('should shim rules with quoted content', () => { const styleStr = 'div {background-image: url("a.jpg"); color: red;}'; const css = shim(styleStr, 'contenta'); @@ -137,4 +113,33 @@ describe('ShadowCss', () => { const css = shim(styleStr, 'contenta'); expect(css).toEqualCss('div[contenta]::after { content:"{}"}'); }); + + describe('comments', () => { + // Comments should be kept in the same position as otherwise inline sourcemaps break due to + // shift in lines. + it('should not strip comments', () => { + expect(shim('/* b {c} */ b {c}', 'contenta')).toEqualCss('/* b {c} */ b[contenta] {c}'); + }); + + it('should not strip multiline comments', () => { + expect(shim('/* b {c}\n */ b {c}', 'contenta')).toEqualCss('/* b {c}\n */ b[contenta] {c}'); + }); + + it('should keep comments in the original position', () => { + expect(shim('/* b {c} */ b {c} /* a {c} */ a {c}', 'contenta')) + .toEqualCss('/* b {c} */ b[contenta] {c} /* a {c} */ a[contenta] {c}'); + }); + + it('should keep sourceMappingURL comments', () => { + expect(shim('b {c} /*# sourceMappingURL=data:x */', 'contenta')) + .toEqualCss('b[contenta] {c} /*# sourceMappingURL=data:x */'); + expect(shim('b {c}/* #sourceMappingURL=data:x */', 'contenta')) + .toEqualCss('b[contenta] {c}/* #sourceMappingURL=data:x */'); + }); + + it('should keep sourceURL comments', () => { + expect(shim('/*# sourceMappingURL=data:x */b {c}/*# sourceURL=xxx */', 'contenta')) + .toEqualCss('/*# sourceMappingURL=data:x */b[contenta] {c}/*# sourceURL=xxx */'); + }); + }); });