| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 | /* Stencil Client Platform v2.18.1 | MIT Licensed | https://stenciljs.com *//** * @license * Copyright Google Inc. 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.io/license * * This file is a port of shadowCSS from webcomponents.js to TypeScript. * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js * https://github.com/angular/angular/blob/master/packages/compiler/src/shadow_css.ts */const safeSelector = (selector) => {    const placeholders = [];    let index = 0;    // Replaces attribute selectors with placeholders.    // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.    selector = selector.replace(/(\[[^\]]*\])/g, (_, keep) => {        const replaceBy = `__ph-${index}__`;        placeholders.push(keep);        index++;        return replaceBy;    });    // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.    // WS and "+" would otherwise be interpreted as selector separators.    const content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {        const replaceBy = `__ph-${index}__`;        placeholders.push(exp);        index++;        return pseudo + replaceBy;    });    const ss = {        content,        placeholders,    };    return ss;};const restoreSafeSelector = (placeholders, content) => {    return content.replace(/__ph-(\d+)__/g, (_, index) => placeholders[+index]);};const _polyfillHost = '-shadowcsshost';const _polyfillSlotted = '-shadowcssslotted';// note: :host-context pre-processed to -shadowcsshostcontext.const _polyfillHostContext = '-shadowcsscontext';const _parenSuffix = ')(?:\\((' + '(?:\\([^)(]*\\)|[^)(]*)+?' + ')\\))?([^,{]*)';const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim');const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim');const _cssColonSlottedRe = new RegExp('(' + _polyfillSlotted + _parenSuffix, 'gim');const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;const _shadowDOMSelectorsRe = [/::shadow/g, /::content/g];const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$';const _polyfillHostRe = /-shadowcsshost/gim;const _colonHostRe = /:host/gim;const _colonSlottedRe = /::slotted/gim;const _colonHostContextRe = /:host-context/gim;const _commentRe = /\/\*\s*[\s\S]*?\*\//g;const stripComments = (input) => {    return input.replace(_commentRe, '');};const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;const extractCommentsWithHash = (input) => {    return input.match(_commentWithHashRe) || [];};const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;const _curlyRe = /([{}])/g;const _selectorPartsRe = /(^.*?[^\\])??((:+)(.*)|$)/;const OPEN_CURLY = '{';const CLOSE_CURLY = '}';const BLOCK_PLACEHOLDER = '%BLOCK%';const processRules = (input, ruleCallback) => {    const inputWithEscapedBlocks = escapeBlocks(input);    let nextBlockIndex = 0;    return inputWithEscapedBlocks.escapedString.replace(_ruleRe, (...m) => {        const selector = m[2];        let content = '';        let suffix = m[4];        let contentPrefix = '';        if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {            content = inputWithEscapedBlocks.blocks[nextBlockIndex++];            suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);            contentPrefix = '{';        }        const cssRule = {            selector,            content,        };        const rule = ruleCallback(cssRule);        return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;    });};const escapeBlocks = (input) => {    const inputParts = input.split(_curlyRe);    const resultParts = [];    const escapedBlocks = [];    let bracketCount = 0;    let currentBlockParts = [];    for (let partIndex = 0; partIndex < inputParts.length; partIndex++) {        const part = inputParts[partIndex];        if (part === CLOSE_CURLY) {            bracketCount--;        }        if (bracketCount > 0) {            currentBlockParts.push(part);        }        else {            if (currentBlockParts.length > 0) {                escapedBlocks.push(currentBlockParts.join(''));                resultParts.push(BLOCK_PLACEHOLDER);                currentBlockParts = [];            }            resultParts.push(part);        }        if (part === OPEN_CURLY) {            bracketCount++;        }    }    if (currentBlockParts.length > 0) {        escapedBlocks.push(currentBlockParts.join(''));        resultParts.push(BLOCK_PLACEHOLDER);    }    const strEscapedBlocks = {        escapedString: resultParts.join(''),        blocks: escapedBlocks,    };    return strEscapedBlocks;};const insertPolyfillHostInCssText = (selector) => {    selector = selector        .replace(_colonHostContextRe, _polyfillHostContext)        .replace(_colonHostRe, _polyfillHost)        .replace(_colonSlottedRe, _polyfillSlotted);    return selector;};const convertColonRule = (cssText, regExp, partReplacer) => {    // m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule    return cssText.replace(regExp, (...m) => {        if (m[2]) {            const parts = m[2].split(',');            const r = [];            for (let i = 0; i < parts.length; i++) {                const p = parts[i].trim();                if (!p)                    break;                r.push(partReplacer(_polyfillHostNoCombinator, p, m[3]));            }            return r.join(',');        }        else {            return _polyfillHostNoCombinator + m[3];        }    });};const colonHostPartReplacer = (host, part, suffix) => {    return host + part.replace(_polyfillHost, '') + suffix;};const convertColonHost = (cssText) => {    return convertColonRule(cssText, _cssColonHostRe, colonHostPartReplacer);};const colonHostContextPartReplacer = (host, part, suffix) => {    if (part.indexOf(_polyfillHost) > -1) {        return colonHostPartReplacer(host, part, suffix);    }    else {        return host + part + suffix + ', ' + part + ' ' + host + suffix;    }};const convertColonSlotted = (cssText, slotScopeId) => {    const slotClass = '.' + slotScopeId + ' > ';    const selectors = [];    cssText = cssText.replace(_cssColonSlottedRe, (...m) => {        if (m[2]) {            const compound = m[2].trim();            const suffix = m[3];            const slottedSelector = slotClass + compound + suffix;            let prefixSelector = '';            for (let i = m[4] - 1; i >= 0; i--) {                const char = m[5][i];                if (char === '}' || char === ',') {                    break;                }                prefixSelector = char + prefixSelector;            }            const orgSelector = prefixSelector + slottedSelector;            const addedSelector = `${prefixSelector.trimRight()}${slottedSelector.trim()}`;            if (orgSelector.trim() !== addedSelector.trim()) {                const updatedSelector = `${addedSelector}, ${orgSelector}`;                selectors.push({                    orgSelector,                    updatedSelector,                });            }            return slottedSelector;        }        else {            return _polyfillHostNoCombinator + m[3];        }    });    return {        selectors,        cssText,    };};const convertColonHostContext = (cssText) => {    return convertColonRule(cssText, _cssColonHostContextRe, colonHostContextPartReplacer);};const convertShadowDOMSelectors = (cssText) => {    return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);};const makeScopeMatcher = (scopeSelector) => {    const lre = /\[/g;    const rre = /\]/g;    scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');    return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');};const selectorNeedsScoping = (selector, scopeSelector) => {    const re = makeScopeMatcher(scopeSelector);    return !re.test(selector);};const injectScopingSelector = (selector, scopingSelector) => {    return selector.replace(_selectorPartsRe, (_, before = '', _colonGroup, colon = '', after = '') => {        return before + scopingSelector + colon + after;    });};const applySimpleSelectorScope = (selector, scopeSelector, hostSelector) => {    // In Android browser, the lastIndex is not reset when the regex is used in String.replace()    _polyfillHostRe.lastIndex = 0;    if (_polyfillHostRe.test(selector)) {        const replaceBy = `.${hostSelector}`;        return selector            .replace(_polyfillHostNoCombinatorRe, (_, selector) => injectScopingSelector(selector, replaceBy))            .replace(_polyfillHostRe, replaceBy + ' ');    }    return scopeSelector + ' ' + selector;};const applyStrictSelectorScope = (selector, scopeSelector, hostSelector) => {    const isRe = /\[is=([^\]]*)\]/g;    scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);    const className = '.' + scopeSelector;    const _scopeSelectorPart = (p) => {        let scopedP = p.trim();        if (!scopedP) {            return '';        }        if (p.indexOf(_polyfillHostNoCombinator) > -1) {            scopedP = applySimpleSelectorScope(p, scopeSelector, hostSelector);        }        else {            // remove :host since it should be unnecessary            const t = p.replace(_polyfillHostRe, '');            if (t.length > 0) {                scopedP = injectScopingSelector(t, className);            }        }        return scopedP;    };    const safeContent = safeSelector(selector);    selector = safeContent.content;    let scopedSelector = '';    let startIndex = 0;    let res;    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    // `:host-context(div)` is transformed to    // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`    // the `div` is not part of the component in the 2nd selectors and should not be scoped.    // Historically `component-tag:host` was matching the component so we also want to preserve    // this behavior to avoid breaking legacy apps (it should not match).    // The behavior should be:    // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)    // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a    //   `:host-context(tag)`)    const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;    // 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];        const part = selector.slice(startIndex, res.index).trim();        shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;        const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;        scopedSelector += `${scopedPart} ${separator} `;        startIndex = sep.lastIndex;    }    const part = selector.substring(startIndex);    shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;    scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;    // replace the placeholders with their original values    return restoreSafeSelector(safeContent.placeholders, scopedSelector);};const scopeSelector = (selector, scopeSelectorText, hostSelector, slotSelector) => {    return selector        .split(',')        .map((shallowPart) => {        if (slotSelector && shallowPart.indexOf('.' + slotSelector) > -1) {            return shallowPart.trim();        }        if (selectorNeedsScoping(shallowPart, scopeSelectorText)) {            return applyStrictSelectorScope(shallowPart, scopeSelectorText, hostSelector).trim();        }        else {            return shallowPart.trim();        }    })        .join(', ');};const scopeSelectors = (cssText, scopeSelectorText, hostSelector, slotSelector, commentOriginalSelector) => {    return processRules(cssText, (rule) => {        let selector = rule.selector;        let content = rule.content;        if (rule.selector[0] !== '@') {            selector = scopeSelector(rule.selector, scopeSelectorText, hostSelector, slotSelector);        }        else if (rule.selector.startsWith('@media') ||            rule.selector.startsWith('@supports') ||            rule.selector.startsWith('@page') ||            rule.selector.startsWith('@document')) {            content = scopeSelectors(rule.content, scopeSelectorText, hostSelector, slotSelector);        }        const cssRule = {            selector: selector.replace(/\s{2,}/g, ' ').trim(),            content,        };        return cssRule;    });};const scopeCssText = (cssText, scopeId, hostScopeId, slotScopeId, commentOriginalSelector) => {    cssText = insertPolyfillHostInCssText(cssText);    cssText = convertColonHost(cssText);    cssText = convertColonHostContext(cssText);    const slotted = convertColonSlotted(cssText, slotScopeId);    cssText = slotted.cssText;    cssText = convertShadowDOMSelectors(cssText);    if (scopeId) {        cssText = scopeSelectors(cssText, scopeId, hostScopeId, slotScopeId);    }    cssText = cssText.replace(/-shadowcsshost-no-combinator/g, `.${hostScopeId}`);    cssText = cssText.replace(/>\s*\*\s+([^{, ]+)/gm, ' $1 ');    return {        cssText: cssText.trim(),        slottedSelectors: slotted.selectors,    };};const scopeCss = (cssText, scopeId, commentOriginalSelector) => {    const hostScopeId = scopeId + '-h';    const slotScopeId = scopeId + '-s';    const commentsWithHash = extractCommentsWithHash(cssText);    cssText = stripComments(cssText);    const orgSelectors = [];    if (commentOriginalSelector) {        const processCommentedSelector = (rule) => {            const placeholder = `/*!@___${orgSelectors.length}___*/`;            const comment = `/*!@${rule.selector}*/`;            orgSelectors.push({ placeholder, comment });            rule.selector = placeholder + rule.selector;            return rule;        };        cssText = processRules(cssText, (rule) => {            if (rule.selector[0] !== '@') {                return processCommentedSelector(rule);            }            else if (rule.selector.startsWith('@media') ||                rule.selector.startsWith('@supports') ||                rule.selector.startsWith('@page') ||                rule.selector.startsWith('@document')) {                rule.content = processRules(rule.content, processCommentedSelector);                return rule;            }            return rule;        });    }    const scoped = scopeCssText(cssText, scopeId, hostScopeId, slotScopeId);    cssText = [scoped.cssText, ...commentsWithHash].join('\n');    if (commentOriginalSelector) {        orgSelectors.forEach(({ placeholder, comment }) => {            cssText = cssText.replace(placeholder, comment);        });    }    scoped.slottedSelectors.forEach((slottedSelector) => {        cssText = cssText.replace(slottedSelector.orgSelector, slottedSelector.updatedSelector);    });    return cssText;};export { scopeCss };
 |