{"version":3,"file":"styled-components.min.js","sources":["../src/models/StyleTags.js"],"sourcesContent":["// @flow\n/* eslint-disable flowtype/object-type-delimiter */\n/* eslint-disable react/prop-types */\n\nimport React, { type Element } from 'react';\nimport { IS_BROWSER, DISABLE_SPEEDY, SC_ATTR, SC_VERSION_ATTR } from '../constants';\nimport StyledError from '../utils/error';\nimport { type ExtractedComp } from '../utils/extractCompsFromCSS';\nimport { splitByRules } from '../utils/stringifyRules';\nimport getNonce from '../utils/nonce';\n\nimport {\n type Names,\n addNameForId,\n resetIdNames,\n hasNameForId,\n stringifyNames,\n cloneNames,\n} from '../utils/styleNames';\n\nimport { sheetForTag, safeInsertRule, deleteRules } from '../utils/insertRuleHelpers';\n\ndeclare var __VERSION__: string;\n\nexport interface Tag<T> {\n // $FlowFixMe: Doesn't seem to accept any combination w/ HTMLStyleElement for some reason\n styleTag: HTMLStyleElement | null;\n /* lists all ids of the tag */\n getIds(): string[];\n /* checks whether `name` is already injected for `id` */\n hasNameForId(id: string, name: string): boolean;\n /* inserts a marker to ensure the id's correct position in the sheet */\n insertMarker(id: string): T;\n /* inserts rules according to the ids markers */\n insertRules(id: string, cssRules: string[], name: ?string): void;\n /* removes all rules belonging to the id, keeping the marker around */\n removeRules(id: string): void;\n css(): string;\n toHTML(additionalAttrs: ?string): string;\n toElement(): Element<*>;\n clone(): Tag<T>;\n /* used in server side rendering to indicate that the rules in the tag have been flushed to HTML */\n sealed: boolean;\n}\n\n/* this marker separates component styles and is important for rehydration */\nconst makeTextMarker = id => `\\n/* sc-component-id: ${id} */\\n`;\n\n/* add up all numbers in array up until and including the index */\nconst addUpUntilIndex = (sizes: number[], index: number): number => {\n let totalUpToIndex = 0;\n for (let i = 0; i <= index; i += 1) {\n totalUpToIndex += sizes[i];\n }\n\n return totalUpToIndex;\n};\n\n/* create a new style tag after lastEl */\nconst makeStyleTag = (target: ?HTMLElement, tagEl: ?Node, insertBefore: ?boolean) => {\n const el = document.createElement('style');\n el.setAttribute(SC_ATTR, '');\n el.setAttribute(SC_VERSION_ATTR, __VERSION__);\n\n const nonce = getNonce();\n if (nonce) {\n el.setAttribute('nonce', nonce);\n }\n\n /* Work around insertRule quirk in EdgeHTML */\n el.appendChild(document.createTextNode(''));\n\n if (target && !tagEl) {\n /* Append to target when no previous element was passed */\n target.appendChild(el);\n } else {\n if (!tagEl || !target || !tagEl.parentNode) {\n throw new StyledError(6);\n }\n\n /* Insert new style tag after the previous one */\n tagEl.parentNode.insertBefore(el, insertBefore ? tagEl : tagEl.nextSibling);\n }\n\n return el;\n};\n\n/* takes a css factory function and outputs an html styled tag factory */\nconst wrapAsHtmlTag = (css: () => string, names: Names) => (additionalAttrs: ?string): string => {\n const nonce = getNonce();\n const attrs = [\n nonce && `nonce=\"${nonce}\"`,\n `${SC_ATTR}=\"${stringifyNames(names)}\"`,\n `${SC_VERSION_ATTR}=\"${__VERSION__}\"`,\n additionalAttrs,\n ];\n\n const htmlAttr = attrs.filter(Boolean).join(' ');\n return `<style ${htmlAttr}>${css()}</style>`;\n};\n\n/* takes a css factory function and outputs an element factory */\nconst wrapAsElement = (css: () => string, names: Names) => () => {\n const props = {\n [SC_ATTR]: stringifyNames(names),\n [SC_VERSION_ATTR]: __VERSION__,\n };\n\n const nonce = getNonce();\n if (nonce) {\n // $FlowFixMe\n props.nonce = nonce;\n }\n\n // eslint-disable-next-line react/no-danger\n return <style {...props} dangerouslySetInnerHTML={{ __html: css() }} />;\n};\n\nconst getIdsFromMarkersFactory = (markers: Object) => (): string[] => Object.keys(markers);\n\n/* speedy tags utilise insertRule */\nconst makeSpeedyTag = (el: HTMLStyleElement, getImportRuleTag: ?() => Tag<any>): Tag<number> => {\n const names: Names = (Object.create(null): Object);\n const markers = Object.create(null);\n const sizes: number[] = [];\n\n const extractImport = getImportRuleTag !== undefined;\n /* indicates whether getImportRuleTag was called */\n let usedImportRuleTag = false;\n\n const insertMarker = id => {\n const prev = markers[id];\n if (prev !== undefined) {\n return prev;\n }\n\n markers[id] = sizes.length;\n sizes.push(0);\n resetIdNames(names, id);\n\n return markers[id];\n };\n\n const insertRules = (id, cssRules, name) => {\n const marker = insertMarker(id);\n const sheet = sheetForTag(el);\n const insertIndex = addUpUntilIndex(sizes, marker);\n\n let injectedRules = 0;\n const importRules = [];\n const cssRulesSize = cssRules.length;\n\n for (let i = 0; i < cssRulesSize; i += 1) {\n const cssRule = cssRules[i];\n let mayHaveImport = extractImport; /* @import rules are reordered to appear first */\n if (mayHaveImport && cssRule.indexOf('@import') !== -1) {\n importRules.push(cssRule);\n } else if (safeInsertRule(sheet, cssRule, insertIndex + injectedRules)) {\n mayHaveImport = false;\n injectedRules += 1;\n }\n }\n\n if (extractImport && importRules.length > 0) {\n usedImportRuleTag = true;\n // $FlowFixMe\n getImportRuleTag().insertRules(`${id}-import`, importRules);\n }\n\n sizes[marker] += injectedRules; /* add up no of injected rules */\n addNameForId(names, id, name);\n };\n\n const removeRules = id => {\n const marker = markers[id];\n if (marker === undefined) return;\n\n const size = sizes[marker];\n const sheet = sheetForTag(el);\n const removalIndex = addUpUntilIndex(sizes, marker) - 1;\n deleteRules(sheet, removalIndex, size);\n sizes[marker] = 0;\n resetIdNames(names, id);\n\n if (extractImport && usedImportRuleTag) {\n // $FlowFixMe\n getImportRuleTag().removeRules(`${id}-import`);\n }\n };\n\n const css = () => {\n const { cssRules } = sheetForTag(el);\n let str = '';\n\n // eslint-disable-next-line guard-for-in\n for (const id in markers) {\n str += makeTextMarker(id);\n const marker = markers[id];\n const end = addUpUntilIndex(sizes, marker);\n const size = sizes[marker];\n for (let i = end - size; i < end; i += 1) {\n const rule = cssRules[i];\n if (rule !== undefined) {\n str += rule.cssText;\n }\n }\n }\n\n return str;\n };\n\n return {\n clone() {\n throw new StyledError(5);\n },\n css,\n getIds: getIdsFromMarkersFactory(markers),\n hasNameForId: hasNameForId(names),\n insertMarker,\n insertRules,\n removeRules,\n sealed: false,\n styleTag: el,\n toElement: wrapAsElement(css, names),\n toHTML: wrapAsHtmlTag(css, names),\n };\n};\n\nconst makeTextNode = id => document.createTextNode(makeTextMarker(id));\n\nconst makeBrowserTag = (el: HTMLStyleElement, getImportRuleTag: ?() => Tag<any>): Tag<Text> => {\n const names = (Object.create(null): Object);\n const markers = Object.create(null);\n\n const extractImport = getImportRuleTag !== undefined;\n\n /* indicates whether getImportRuleTag was called */\n let usedImportRuleTag = false;\n\n const insertMarker = id => {\n const prev = markers[id];\n if (prev !== undefined) {\n return prev;\n }\n\n markers[id] = makeTextNode(id);\n el.appendChild(markers[id]);\n names[id] = Object.create(null);\n\n return markers[id];\n };\n\n const insertRules = (id, cssRules, name) => {\n const marker = insertMarker(id);\n const importRules = [];\n const cssRulesSize = cssRules.length;\n\n for (let i = 0; i < cssRulesSize; i += 1) {\n const rule = cssRules[i];\n let mayHaveImport = extractImport;\n if (mayHaveImport && rule.indexOf('@import') !== -1) {\n importRules.push(rule);\n } else {\n mayHaveImport = false;\n const separator = i === cssRulesSize - 1 ? '' : ' ';\n marker.appendData(`${rule}${separator}`);\n }\n }\n\n addNameForId(names, id, name);\n\n if (extractImport && importRules.length > 0) {\n usedImportRuleTag = true;\n // $FlowFixMe\n getImportRuleTag().insertRules(`${id}-import`, importRules);\n }\n };\n\n const removeRules = id => {\n const marker = markers[id];\n if (marker === undefined) return;\n\n /* create new empty text node and replace the current one */\n const newMarker = makeTextNode(id);\n el.replaceChild(newMarker, marker);\n markers[id] = newMarker;\n resetIdNames(names, id);\n\n if (extractImport && usedImportRuleTag) {\n // $FlowFixMe\n getImportRuleTag().removeRules(`${id}-import`);\n }\n };\n\n const css = () => {\n let str = '';\n\n // eslint-disable-next-line guard-for-in\n for (const id in markers) {\n str += markers[id].data;\n }\n\n return str;\n };\n\n return {\n clone() {\n throw new StyledError(5);\n },\n css,\n getIds: getIdsFromMarkersFactory(markers),\n hasNameForId: hasNameForId(names),\n insertMarker,\n insertRules,\n removeRules,\n sealed: false,\n styleTag: el,\n toElement: wrapAsElement(css, names),\n toHTML: wrapAsHtmlTag(css, names),\n };\n};\n\nconst makeServerTag = (namesArg, markersArg): Tag<[string]> => {\n const names = namesArg === undefined ? (Object.create(null): Object) : namesArg;\n const markers = markersArg === undefined ? Object.create(null) : markersArg;\n\n const insertMarker = id => {\n const prev = markers[id];\n if (prev !== undefined) {\n return prev;\n }\n\n return (markers[id] = ['']);\n };\n\n const insertRules = (id, cssRules, name) => {\n const marker = insertMarker(id);\n marker[0] += cssRules.join(' ');\n addNameForId(names, id, name);\n };\n\n const removeRules = id => {\n const marker = markers[id];\n if (marker === undefined) return;\n marker[0] = '';\n resetIdNames(names, id);\n };\n\n const css = () => {\n let str = '';\n // eslint-disable-next-line guard-for-in\n for (const id in markers) {\n const cssForId = markers[id][0];\n if (cssForId) {\n str += makeTextMarker(id) + cssForId;\n }\n }\n return str;\n };\n\n const clone = () => {\n const namesClone = cloneNames(names);\n const markersClone = Object.create(null);\n\n // eslint-disable-next-line guard-for-in\n for (const id in markers) {\n markersClone[id] = [markers[id][0]];\n }\n\n return makeServerTag(namesClone, markersClone);\n };\n\n const tag = {\n clone,\n css,\n getIds: getIdsFromMarkersFactory(markers),\n hasNameForId: hasNameForId(names),\n insertMarker,\n insertRules,\n removeRules,\n sealed: false,\n styleTag: null,\n toElement: wrapAsElement(css, names),\n toHTML: wrapAsHtmlTag(css, names),\n };\n\n return tag;\n};\n\nexport const makeTag = (\n target: ?HTMLElement,\n tagEl: ?HTMLStyleElement,\n forceServer?: boolean,\n insertBefore?: boolean,\n getImportRuleTag?: () => Tag<any>\n): Tag<any> => {\n if (IS_BROWSER && !forceServer) {\n const el = makeStyleTag(target, tagEl, insertBefore);\n\n if (DISABLE_SPEEDY) {\n return makeBrowserTag(el, getImportRuleTag);\n } else {\n return makeSpeedyTag(el, getImportRuleTag);\n }\n }\n\n return makeServerTag();\n};\n\nexport const rehydrate = (\n tag: Tag<any>,\n els: HTMLStyleElement[],\n extracted: ExtractedComp[]\n): void => {\n /* add all extracted components to the new tag */\n for (let i = 0, len = extracted.length; i < len; i += 1) {\n const { componentId, cssFromDOM } = extracted[i];\n const cssRules = splitByRules(cssFromDOM);\n tag.insertRules(componentId, cssRules);\n }\n\n /* remove old HTMLStyleElements, since they have been rehydrated */\n for (let i = 0, len = els.length; i < len; i += 1) {\n const el = els[i];\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n }\n};\n"],"names":["__VERSION__","i"],"mappings":"2woBA2DiDA,k+FAiWtCC"}