function sheetForTag(tag: HTMLStyleElement): CSSStyleSheet {
if (tag.sheet) {
return tag.sheet
}
for (let i = 0; i < document.styleSheets.length; i++) {
if (document.styleSheets[i].ownerNode === tag) {
return document.styleSheets[i]
}
}
}
export type Options = {
nonce?: string,
key: string,
container: Node,
speedy?: boolean,
prepend?: boolean,
insertionPoint?: HTMLElement
}
function createStyleElement(options: {
key: string,
nonce: string | void
}): HTMLStyleElement {
let tag = document.createElement('style')
tag.setAttribute('data-emotion', options.key)
if (options.nonce !== undefined) {
tag.setAttribute('nonce', options.nonce)
}
tag.appendChild(document.createTextNode(''))
tag.setAttribute('data-s', '')
return tag
}
export class StyleSheet {
isSpeedy: boolean
ctr: number
tags: HTMLStyleElement[]
container: Node
key: string
nonce: string | void
prepend: boolean | void
before: Element | null
insertionPoint: HTMLElement | void
constructor(options: Options) {
this.isSpeedy =
options.speedy === undefined
? process.env.NODE_ENV === 'production'
: options.speedy
this.tags = []
this.ctr = 0
this.nonce = options.nonce
this.key = options.key
this.container = options.container
this.prepend = options.prepend
this.insertionPoint = options.insertionPoint
this.before = null
}
_insertTag = (tag: HTMLStyleElement) => {
let before
if (this.tags.length === 0) {
if (this.insertionPoint) {
before = this.insertionPoint.nextSibling
} else if (this.prepend) {
before = this.container.firstChild
} else {
before = this.before
}
} else {
before = this.tags[this.tags.length - 1].nextSibling
}
this.container.insertBefore(tag, before)
this.tags.push(tag)
}
hydrate(nodes: HTMLStyleElement[]) {
nodes.forEach(this._insertTag)
}
insert(rule: string) {
if (this.ctr % (this.isSpeedy ? 65000 : 1) === 0) {
this._insertTag(createStyleElement(this))
}
const tag = this.tags[this.tags.length - 1]
if (process.env.NODE_ENV !== 'production') {
const isImportRule =
rule.charCodeAt(0) === 64 && rule.charCodeAt(1) === 105
if (isImportRule && (this: any)._alreadyInsertedOrderInsensitiveRule) {
console.error(
`You're attempting to insert the following rule:\n` +
rule +
'\n\n`@import` rules must be before all other types of rules in a stylesheet but other rules have already been inserted. Please ensure that `@import` rules are before all other rules.'
)
}
;(this: any)._alreadyInsertedOrderInsensitiveRule =
(this: any)._alreadyInsertedOrderInsensitiveRule || !isImportRule
}
if (this.isSpeedy) {
const sheet = sheetForTag(tag)
try {
sheet.insertRule(rule, sheet.cssRules.length)
} catch (e) {
if (
process.env.NODE_ENV !== 'production' &&
!/:(-moz-placeholder|-moz-focus-inner|-moz-focusring|-ms-input-placeholder|-moz-read-write|-moz-read-only|-ms-clear|-ms-expand|-ms-reveal){/.test(
rule
)
) {
console.error(
`There was a problem inserting the following rule: "${rule}"`,
e
)
}
}
} else {
tag.appendChild(document.createTextNode(rule))
}
this.ctr++
}
flush() {
this.tags.forEach(tag => tag.parentNode && tag.parentNode.removeChild(tag))
this.tags = []
this.ctr = 0
if (process.env.NODE_ENV !== 'production') {
;(this: any)._alreadyInsertedOrderInsensitiveRule = false
}
}
}