Skip to content

Commit

Permalink
feat: preserve classes created by explicit tw calls during SSR
Browse files Browse the repository at this point in the history
  • Loading branch information
sastan committed May 5, 2022
1 parent d5765a7 commit fe88051
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 167 deletions.
42 changes: 42 additions & 0 deletions .changeset/little-pets-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
'twind': patch
---

feat: preserve classes created by explicit `tw` calls during SSR

Previously `inline` and `extract` cleared the `tw` instance before parsing the html assuming that all classes are available via `class` attributes. That led to missing styles from `injectGlobal` or explicit `tw` calls.

This change introduces a `snaphot` method on `tw` and sheet instances which allows to preserve the classes that are created by explicit `tw` calls.

**Default Mode** _(nothing changed here)_

```js
import { inline } from 'twind'

function render() {
return inline(renderApp())
}
```

**Library Mode**

```js
import { tw, stringify } from 'twind'

function render() {
// remember global classes
const restore = tw.snapshot()

// generated html
const html = renderApp()

// create CSS
const css = stringify(tw.target)

// restore global classes
restore()

// inject as last element into the head
return html.replace('</head>', `<style data-twind>${css}</style></head>`)
}
```
60 changes: 0 additions & 60 deletions packages/twind/src/extract.ts

This file was deleted.

4 changes: 1 addition & 3 deletions packages/twind/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@

export * from './animation'
export * from './colors'
export * from './consume'
export * from './css'
export * from './cx'
export * from './define-config'
export * from './extract'
export * from './inject-global'
export * from './inline'
export * from './install'
export * from './keyframes'
export * from './nested'
Expand All @@ -24,5 +21,6 @@ export * from './style'
export * from './twind'
export * from './tx'
export * from './sheets'
export * from './ssr'
export * from './types'
export * from './utils'
83 changes: 0 additions & 83 deletions packages/twind/src/inline.ts

This file was deleted.

53 changes: 46 additions & 7 deletions packages/twind/src/sheets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ export function cssom(element?: CSSStyleSheet | Element | null | false): Sheet<C
return {
target,

snapshot() {
// collect current rules
const rules = Array.from(target.cssRules, (rule) => rule.cssText)

return () => {
// remove all existing rules
this.clear()

// add all snapshot rules back
// eslint-disable-next-line @typescript-eslint/unbound-method
rules.forEach(this.insert as (cssText: string, index: number) => void)
}
},

clear() {
// remove all added rules
for (let index = target.cssRules.length; index--; ) {
Expand All @@ -32,10 +46,10 @@ export function cssom(element?: CSSStyleSheet | Element | null | false): Sheet<C
target.ownerNode?.remove()
},

insert(css, index) {
insert(cssText, index) {
try {
// Insert
target.insertRule(css, index)
target.insertRule(cssText, index)
} catch (error) {
// Empty rule to keep index valid — not using `*{}` as that would show up in all rules (DX)
target.insertRule(':root{}', index)
Expand All @@ -44,8 +58,8 @@ export function cssom(element?: CSSStyleSheet | Element | null | false): Sheet<C
// lets filter them to prevent unnecessary warnings
// ::-moz-focus-inner
// :-moz-focusring
if (!/:-[mwo]/.test(css)) {
console.warn(error, css)
if (!/:-[mwo]/.test(cssText)) {
console.warn(error, cssText)
}
}
},
Expand All @@ -60,6 +74,20 @@ export function dom(element?: Element | null | false): Sheet<HTMLStyleElement> {
return {
target,

snapshot() {
// collect current rules
const rules = Array.from(target.childNodes, (node) => node.textContent as string)

return () => {
// remove all existing rules
this.clear()

// add all snapshot rules back
// eslint-disable-next-line @typescript-eslint/unbound-method
rules.forEach(this.insert as (cssText: string, index: number) => void)
}
},

clear() {
target.textContent = ''
},
Expand All @@ -68,8 +96,8 @@ export function dom(element?: Element | null | false): Sheet<HTMLStyleElement> {
target.remove()
},

insert(css, index) {
target.insertBefore(document.createTextNode(css), target.childNodes[index] || null)
insert(cssText, index) {
target.insertBefore(document.createTextNode(cssText), target.childNodes[index] || null)
},

resume: noop,
Expand All @@ -78,9 +106,20 @@ export function dom(element?: Element | null | false): Sheet<HTMLStyleElement> {

export function virtual(includeResumeData?: boolean): Sheet<string[]> {
const target: string[] = []

return {
target,

snapshot() {
// collect current rules
const rules = [...target]

return () => {
// remove all existing rules and add all snapshot rules back
target.splice(0, target.length, ...rules)
}
},

clear() {
target.length = 0
},
Expand Down Expand Up @@ -128,7 +167,7 @@ export function stringify(target: unknown): string {
// string[] | CSSStyleSheet | HTMLStyleElement
return (
// prefer the raw text content of a CSSStyleSheet as it may include the resume data
((target as CSSStyleSheet).ownerNode || (target as HTMLStyleElement))?.textContent ||
((target as CSSStyleSheet).ownerNode || (target as HTMLStyleElement)).textContent ||
((target as CSSStyleSheet).cssRules
? Array.from((target as CSSStyleSheet).cssRules, (rule) => rule.cssText)
: asArray(target)
Expand Down
Loading

0 comments on commit fe88051

Please sign in to comment.