Skip to content

Commit

Permalink
perf: use typeof window to detect browser (#810)
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Sep 2, 2022
1 parent 66bd5f0 commit 2a31e73
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 34 deletions.
26 changes: 11 additions & 15 deletions src/lib/stylesheet.js
Expand Up @@ -10,11 +10,7 @@ const isProd =
const isString = o => Object.prototype.toString.call(o) === '[object String]'

export default class StyleSheet {
constructor({
name = 'stylesheet',
optimizeForSpeed = isProd,
isBrowser = typeof window !== 'undefined'
} = {}) {
constructor({ name = 'stylesheet', optimizeForSpeed = isProd } = {}) {
invariant(isString(name), '`name` must be a string')
this._name = name
this._deletedRulePlaceholder = `#${name}-deleted-rule____{}`
Expand All @@ -24,15 +20,14 @@ export default class StyleSheet {
'`optimizeForSpeed` must be a boolean'
)
this._optimizeForSpeed = optimizeForSpeed
this._isBrowser = isBrowser

this._serverSheet = undefined
this._tags = []
this._injected = false
this._rulesCount = 0

const node =
this._isBrowser && document.querySelector('meta[property="csp-nonce"]')
typeof window !== 'undefined' &&
document.querySelector('meta[property="csp-nonce"]')
this._nonce = node ? node.getAttribute('content') : null
}

Expand All @@ -58,7 +53,7 @@ export default class StyleSheet {
inject() {
invariant(!this._injected, 'sheet already injected')
this._injected = true
if (this._isBrowser && this._optimizeForSpeed) {
if (typeof window !== 'undefined' && this._optimizeForSpeed) {
this._tags[0] = this.makeStyleTag(this._name)
this._optimizeForSpeed = 'insertRule' in this.getSheet()
if (!this._optimizeForSpeed) {
Expand Down Expand Up @@ -112,7 +107,7 @@ export default class StyleSheet {
insertRule(rule, index) {
invariant(isString(rule), '`insertRule` accepts only strings')

if (!this._isBrowser) {
if (typeof window === 'undefined') {
if (typeof index !== 'number') {
index = this._serverSheet.cssRules.length
}
Expand Down Expand Up @@ -149,8 +144,9 @@ export default class StyleSheet {
}

replaceRule(index, rule) {
if (this._optimizeForSpeed || !this._isBrowser) {
const sheet = this._isBrowser ? this.getSheet() : this._serverSheet
if (this._optimizeForSpeed || typeof window === 'undefined') {
const sheet =
typeof window !== 'undefined' ? this.getSheet() : this._serverSheet
if (!rule.trim()) {
rule = this._deletedRulePlaceholder
}
Expand Down Expand Up @@ -184,7 +180,7 @@ export default class StyleSheet {
}

deleteRule(index) {
if (!this._isBrowser) {
if (typeof window === 'undefined') {
this._serverSheet.deleteRule(index)
return
}
Expand All @@ -202,7 +198,7 @@ export default class StyleSheet {
flush() {
this._injected = false
this._rulesCount = 0
if (this._isBrowser) {
if (typeof window !== 'undefined') {
this._tags.forEach(tag => tag && tag.parentNode.removeChild(tag))
this._tags = []
} else {
Expand All @@ -212,7 +208,7 @@ export default class StyleSheet {
}

cssRules() {
if (!this._isBrowser) {
if (typeof window === 'undefined') {
return this._serverSheet.cssRules
}

Expand Down
10 changes: 2 additions & 8 deletions src/stylesheet-registry.js
Expand Up @@ -19,11 +19,7 @@ function mapRulesToStyle(cssRules, options = {}) {
})
}
export class StyleSheetRegistry {
constructor({
styleSheet = null,
optimizeForSpeed = false,
isBrowser = typeof window !== 'undefined'
} = {}) {
constructor({ styleSheet = null, optimizeForSpeed = false } = {}) {
this._sheet =
styleSheet ||
new DefaultStyleSheet({
Expand All @@ -37,8 +33,6 @@ export class StyleSheetRegistry {
this._optimizeForSpeed = this._sheet.isOptimizeForSpeed()
}

this._isBrowser = isBrowser

this._fromServer = undefined
this._indices = {}
this._instancesCounts = {}
Expand All @@ -51,7 +45,7 @@ export class StyleSheetRegistry {
this._optimizeForSpeed = this._sheet.isOptimizeForSpeed()
}

if (this._isBrowser && !this._fromServer) {
if (typeof window !== 'undefined' && !this._fromServer) {
this._fromServer = this.selectFromServer()
this._instancesCounts = Object.keys(this._fromServer).reduce(
(acc, tagName) => {
Expand Down
6 changes: 3 additions & 3 deletions test/helpers/with-mock.js
Expand Up @@ -11,9 +11,9 @@ export default function withMock(mockFn, testFn) {
}

export function withMockDocument(t) {
const originalDocument = global.document
const originalDocument = globalThis.document
// We need to stub a document in order to simulate the meta tag
global.document = {
globalThis.document = {
querySelector(query) {
t.is(query, 'meta[property="csp-nonce"]')
return {
Expand All @@ -26,6 +26,6 @@ export function withMockDocument(t) {
}

return () => {
global.document = originalDocument
globalThis.document = originalDocument
}
}
47 changes: 42 additions & 5 deletions test/stylesheet-registry.js
Expand Up @@ -7,7 +7,7 @@ import makeSheet, { invalidRules } from './stylesheet'
import withMock, { withMockDocument } from './helpers/with-mock'
import { computeId, computeSelector } from '../src/lib/hash'

function makeRegistry(options = { optimizeForSpeed: true, isBrowser: true }) {
function makeRegistry(options = { optimizeForSpeed: true }) {
const registry = new StyleSheetRegistry({
styleSheet: makeSheet(options),
...options
Expand All @@ -32,7 +32,12 @@ test(
]

options.forEach(options => {
if (options.isBrowser) {
globalThis.window = globalThis
}

const registry = makeRegistry(options)

registry.add({
id: '123',
children: options.optimizeForSpeed ? [cssRule] : cssRule
Expand Down Expand Up @@ -68,13 +73,19 @@ test(
['jsx-456', 'div { color: red }p { color: red }']
])
}

if (options.isBrowser) {
delete globalThis.window
}
})
})
)

test(
'add - filters out invalid rules (index `-1`)',
withMock(withMockDocument, t => {
globalThis.window = globalThis

const registry = makeRegistry()

// Insert a valid rule
Expand All @@ -90,12 +101,16 @@ test(
['jsx-123', 'div { color: red }'],
['jsx-678', 'div { color: red }']
])

delete globalThis.window
})
)

test(
'it does not throw when inserting an invalid rule',
withMock(withMockDocument, t => {
globalThis.window = globalThis

const registry = makeRegistry()

// Insert a valid rule
Expand All @@ -107,11 +122,13 @@ test(
})

t.deepEqual(registry.cssRules(), [['jsx-123', 'div { color: red }']])

delete globalThis.window
})
)

test('add - sanitizes dynamic CSS on the server', t => {
const registry = makeRegistry({ optimizeForSpeed: false, isBrowser: false })
const registry = makeRegistry({ optimizeForSpeed: false })

registry.add({
id: '123',
Expand All @@ -130,9 +147,9 @@ test('add - sanitizes dynamic CSS on the server', t => {
})

test('add - nonce is properly fetched from meta tag', t => {
const originalDocument = global.document
const originalDocument = globalThis.document
// We need to stub a document in order to simulate the meta tag
global.document = {
globalThis.document = {
querySelector(query) {
t.is(query, 'meta[property="csp-nonce"]')
return {
Expand All @@ -144,12 +161,16 @@ test('add - nonce is properly fetched from meta tag', t => {
}
}

globalThis.window = globalThis

const registry = makeRegistry()
registry.add({ id: '123', children: [cssRule] })

t.is(registry._sheet._nonce, 'test-nonce')

global.document = originalDocument
globalThis.document = originalDocument

delete globalThis.window
})

// registry.remove
Expand All @@ -165,6 +186,10 @@ test(
]

options.forEach(options => {
if (options.isBrowser) {
globalThis.window = globalThis
}

const registry = makeRegistry(options)
registry.add({
id: '123',
Expand Down Expand Up @@ -192,6 +217,10 @@ test(
registry.remove({ id: '345' })
// now the registry should be empty
t.deepEqual(registry.cssRules(), [])

if (options.isBrowser) {
delete globalThis.window
}
})
})
)
Expand All @@ -209,6 +238,10 @@ test(
]

options.forEach(options => {
if (options.isBrowser) {
globalThis.window = globalThis
}

const registry = makeRegistry(options)
registry.add({
id: '123',
Expand Down Expand Up @@ -246,6 +279,10 @@ test(
)
// 123 replaced with 345
t.deepEqual(registry.cssRules(), [['jsx-345', cssRuleAlt]])

if (options.isBrowser) {
delete globalThis.window
}
})
})
)
Expand Down

0 comments on commit 2a31e73

Please sign in to comment.