Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: use typeof window to detect browser #810

Merged
merged 2 commits into from Sep 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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