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 */'); + }); + }); });