diff --git a/index.d.ts b/index.d.ts index 6e8eacef..5705d648 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,4 @@ -// Definitions by: @types/styled-jsx - -import 'react' +import React from 'react' declare module 'react' { interface StyleHTMLAttributes extends HTMLAttributes { @@ -8,3 +6,19 @@ declare module 'react' { global?: boolean } } + +export type StyleRegistry = { + styles(options?: { nonce?: boolean }): JSX.Element + flush(): void + add(props: any): void + remove(props: any): void +} +export function useStyleRegistry(): StyleRegistry +export function StyleRegistry({ + children, + registry +}: { + children: JSX.Element | React.ReactNode + registry?: StyleRegistry +}): JSX.Element +export function createStyleRegistry(): StyleRegistry diff --git a/index.js b/index.js new file mode 100644 index 00000000..2d2f3c0b --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require('./dist') diff --git a/package.json b/package.json index 8ff0a542..7bebfff4 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "dist", "lib", "index.d.ts", - "server.js", - "server.d.ts", + "index.js", "babel.js", "babel-test.js", "style.js", @@ -25,7 +24,7 @@ "test": "ava", "lint": "eslint ./src", "format": "prettier --write \"./{src,test}/**/*.{js,css}\"", - "prepublishOnly": "yarn build && yarn test && yarn lint --quiet" + "prepublishOnly": "rm -rf dist && yarn build && yarn test && yarn lint --quiet" }, "husky": { "hooks": { diff --git a/readme.md b/readme.md index 66e78136..c1531839 100644 --- a/readme.md +++ b/readme.md @@ -8,38 +8,37 @@ Code and docs are for v3 which we highly recommend you to try. Looking for style - [Getting started](#getting-started) - [Configuration options](#configuration-options) - * [`optimizeForSpeed`](#optimizeforspeed) - * [`sourceMaps`](#sourcemaps) - * [`styleModule`](#stylemodule) - * [`vendorPrefixes`](#vendorprefixes) + - [`optimizeForSpeed`](#optimizeforspeed) + - [`sourceMaps`](#sourcemaps) + - [`styleModule`](#stylemodule) + - [`vendorPrefixes`](#vendorprefixes) - [Features](#features) - [How It Works](#how-it-works) - * [Why It Works Like This](#why-it-works-like-this) + - [Why It Works Like This](#why-it-works-like-this) - [Targeting The Root](#targeting-the-root) - [Global styles](#global-styles) - * [One-off global selectors](#one-off-global-selectors) + - [One-off global selectors](#one-off-global-selectors) - [Dynamic styles](#dynamic-styles) - * [Via interpolated dynamic props](#via-interpolated-dynamic-props) - * [Via `className` toggling](#via-classname-toggling) - * [Via inline `style`](#via-inline-style) + - [Via interpolated dynamic props](#via-interpolated-dynamic-props) + - [Via `className` toggling](#via-classname-toggling) + - [Via inline `style`](#via-inline-style) - [Constants](#constants) - [Server-Side Rendering](#server-side-rendering) - * [`styled-jsx/server`](#styled-jsxserver) - [External CSS and styles outside of the component](#external-css-and-styles-outside-of-the-component) - * [External styles](#external-styles) - * [Styles outside of components](#styles-outside-of-components) - * [The `resolve` tag](#the-resolve-tag) - * [Styles in regular CSS files](#styles-in-regular-css-files) + - [External styles](#external-styles) + - [Styles outside of components](#styles-outside-of-components) + - [The `resolve` tag](#the-resolve-tag) + - [Styles in regular CSS files](#styles-in-regular-css-files) - [CSS Preprocessing via Plugins](#css-preprocessing-via-plugins) - * [Plugin options](#plugin-options) - * [Example plugins](#example-plugins) + - [Plugin options](#plugin-options) + - [Example plugins](#example-plugins) - [Rendering in tests](#rendering-in-tests) - [FAQ](#faq) - * [Warning: unknown `jsx` prop on <style> tag](#warning-unknown-jsx-prop-on-style-tag) - * [Can I return an array of components when using React 16?](#can-i-return-an-array-of-components-when-using-react-16) - * [Styling third parties / child components from the parent](#styling-third-parties--child-components-from-the-parent) - * [Some styles are missing in production](https://github.com/zeit/styled-jsx/issues/319#issuecomment-349239326) - * [Build a component library with styled-jsx](#build-a-component-library-with-styled-jsx) + - [Warning: unknown `jsx` prop on <style> tag](#warning-unknown-jsx-prop-on-style-tag) + - [Can I return an array of components when using React 16?](#can-i-return-an-array-of-components-when-using-react-16) + - [Styling third parties / child components from the parent](#styling-third-parties--child-components-from-the-parent) + - [Some styles are missing in production](https://github.com/zeit/styled-jsx/issues/319#issuecomment-349239326) + - [Build a component library with styled-jsx](#build-a-component-library-with-styled-jsx) - [Syntax Highlighting](#syntax-highlighting) ## Getting started @@ -54,9 +53,7 @@ Next, add `styled-jsx/babel` to `plugins` in your babel configuration: ```json { - "plugins": [ - "styled-jsx/babel" - ] + "plugins": ["styled-jsx/babel"] } ``` @@ -67,8 +64,8 @@ export default () => (

only this paragraph will get the style :)

- { /* you can include s here that include - other

s that don't get unexpected styles! */ } + {/* you can include s here that include + other

s that don't get unexpected styles! */}

@@ -195,7 +190,7 @@ the global styles being inserted multiple times. Sometimes it's useful to skip selectors scoping. In order to get a one-off global selector we support `:global()`, inspired by [css-modules](https://github.com/css-modules/css-modules). -This is very useful in order to, for example, generate a *global class* that +This is very useful in order to, for example, generate a _global class_ that you can pass to 3rd-party components. For example, to style `react-select` which supports passing a custom class via `optionClassName`: @@ -209,7 +204,7 @@ export default () => ( /* "div" will be prefixed, but ".react-select" won't */ div :global(.react-select) { - color: red + color: red; } `} @@ -225,18 +220,18 @@ To make a component's visual representation customizable from the outside world Any value that comes from the component's `render` method scope is treated as dynamic. This makes it possible to use `props` and `state` for example. ```jsx -const Button = (props) => ( +const Button = props => ( ) ``` @@ -246,22 +241,22 @@ New styles' injection is optimized to perform well at runtime. That said when your CSS is mostly static we recommend to split it up in static and dynamic styles and use two separate `style` tags so that, when changing, only the dynamic parts are recomputed/rendered. ```jsx -const Button = (props) => ( +const Button = props => ( ) ``` @@ -271,19 +266,19 @@ const Button = (props) => ( The second option is to pass properties that toggle class names. ```jsx -const Button = (props) => ( - ) ``` @@ -299,14 +294,14 @@ Imagine that you wanted to make the padding in the button above completely custo ```jsx const Button = ({ padding, children }) => ( ) ``` @@ -323,14 +318,14 @@ import { invertColor } from '../theme/utils' const Button = ({ children }) => ( ) ``` @@ -339,48 +334,34 @@ Please keep in mind that constants defined outside of the component scope are tr ## Server-Side Rendering -### `styled-jsx/server` - -The main export flushes your styles to an array of `React.Element`: - ```jsx import React from 'react' import ReactDOM from 'react-dom/server' -import flush from 'styled-jsx/server' +import { StyleRegistry, useStyleRegistry } from 'styled-jsx' import App from './app' -export default (req, res) => { - const app = ReactDOM.renderToString() - const styles = flush() - const html = ReactDOM.renderToStaticMarkup( - { styles } - -
- - ) - res.end('' + html) +function Styles() { + const registry = useStyleRegistry() + const styles = registry.styles() + registry.flush() + return <>{styles} } -``` - -We also expose `flushToHTML` to return generated HTML: - -```jsx -import React from 'react' -import ReactDOM from 'react-dom/server' -import { flushToHTML } from 'styled-jsx/server' -import App from './app' export default (req, res) => { const app = ReactDOM.renderToString() - const styles = flushToHTML() - const html = ` - - ${styles} - -
${app}
- - ` - res.end(html) + const html = ReactDOM.renderToStaticMarkup( + + + + + + +
+ + + + ) + res.end('' + html) } ``` @@ -393,13 +374,14 @@ duplicate styles are avoided. Strict [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is supported. You should generate a nonce **per request**. + ```js import nanoid from 'nanoid' const nonce = Buffer.from(nanoid()).toString('base64') //ex: N2M0MDhkN2EtMmRkYi00MTExLWFhM2YtNDhkNTc4NGJhMjA3 ``` -You must then pass a nonce to either `flushToReact({ nonce })` or `flushToHTML({ nonce })` **and** set a `` tag. +You must then pass a nonce to `registry.styles({ nonce })` **and** set a `` tag. Your CSP policy must share the same nonce as well (the header nonce needs to match the html nonce and remain unpredictable). `Content-Security-Policy: default-src 'self'; style-src 'self' 'nonce-N2M0MDhkN2EtMmRkYi00MTExLWFhM2YtNDhkNTc4NGJhMjA3';` @@ -408,9 +390,9 @@ Your CSP policy must share the same nonce as well (the header nonce needs to mat In styled-jsx styles can be defined outside of the component's render method or in separate JavaScript modules using the `styled-jsx/css` library. `styled-jsx/css` exports three tags that can be used to tag your styles: -* `css`, the default export, to define scoped styles. -* `css.global` to define global styles. -* `css.resolve` to define scoped styles that resolve to the scoped `className` and a `styles` element. +- `css`, the default export, to define scoped styles. +- `css.global` to define global styles. +- `css.resolve` to define scoped styles that resolve to the scoped `className` and a `styles` element. #### External styles @@ -421,7 +403,11 @@ In an external file: import css from 'styled-jsx/css' // Scoped styles -export const button = css`button { color: hotpink; }` +export const button = css` + button { + color: hotpink; + } +` // Global styles export const body = css.global`body { margin: 0; }` @@ -432,7 +418,11 @@ export const link = css.resolve`a { color: green; }` // link.styles -> styles element to render inside of your component // Works also with default exports -export default css`div { color: green; }` +export default css` + div { + color: green; + } +` ``` You can then import and use those styles: @@ -445,7 +435,9 @@ export default () => ( - +
) ``` @@ -475,7 +467,11 @@ export default () => (
) -const button = css`button { color: hotpink; }` +const button = css` + button { + color: hotpink; + } +` ``` Like in externals styles `css` doesn't work with dynamic styles. If you have dynamic parts you might want to place them inline inside of your component using a regular ` + {alt} + ) ``` @@ -897,7 +906,7 @@ export default () => ( /* "div" will be prefixed, but ".nested-element" won't */ div > :global(.nested-element) { - color: red + color: red; } `} @@ -914,7 +923,7 @@ There's an [article](https://medium.com/@tomaszmularczyk89/guide-to-building-a-r When working with template literals a common drawback is missing syntax highlighting. The following editors currently have support for highlighting CSS inside ` + {children} + + {/*language=CSS*/} + ) ``` ### Emmet - If you're using Emmet you can add the following snippet to `~/emmet/snippets-styledjsx.json` This will allow you to expand `style-jsx` to a styled-jsx block. +If you're using Emmet you can add the following snippet to `~/emmet/snippets-styledjsx.json` This will allow you to expand `style-jsx` to a styled-jsx block. - ```json - { +```json +{ "html": { "snippets": { "style-jsx": "" @@ -971,18 +980,23 @@ const Button = ({ children }) => ( ``` ### Syntax Highlighting [Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=Divlo.vscode-styled-jsx-syntax) + Launch VS Code Quick Open (⌘+P), paste the following command, and press enter. + ``` ext install Divlo.vscode-styled-jsx-syntax ``` If you use Stylus instead of plain CSS, install [vscode-styled-jsx-stylus](https://marketplace.visualstudio.com/items?itemName=samuelroy.vscode-styled-jsx-stylus) or paste the command below. + ``` ext install vscode-styled-jsx-stylus ``` ### Autocomplete [Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=Divlo.vscode-styled-jsx-languageserver) + Launch VS Code Quick Open (⌘+P), paste the following command, and press enter. + ``` ext install Divlo.vscode-styled-jsx-languageserver ``` @@ -992,6 +1006,7 @@ ext install Divlo.vscode-styled-jsx-languageserver Install [vim-styled-jsx](https://github.com/alampros/vim-styled-jsx) with your plugin manager of choice. ## ESLint + If you're using `eslint-plugin-import`, the `css` import will generate errors, being that it's a "magic" import (not listed in package.json). To avoid these, simply add the following line to your eslint configuration: ``` diff --git a/server.d.ts b/server.d.ts deleted file mode 100644 index 35a9e26a..00000000 --- a/server.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Definitions by: @types/styled-jsx - -import { ReactElement } from 'react' - -declare function flushToHTML(opts?: { nonce?: string }): string -declare function flushToReact(opts?: { - nonce?: string -}): Array> - -export { flushToHTML } -export default flushToReact diff --git a/server.js b/server.js deleted file mode 100644 index 3b4b9b0e..00000000 --- a/server.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/server') diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..9a2e40a4 --- /dev/null +++ b/src/index.js @@ -0,0 +1,5 @@ +export { + StyleRegistry, + createStyleRegistry, + useStyleRegistry +} from './stylesheet-registry' diff --git a/src/lib/hash.js b/src/lib/hash.js new file mode 100644 index 00000000..b8a4318d --- /dev/null +++ b/src/lib/hash.js @@ -0,0 +1,46 @@ +import hashString from 'string-hash' + +const sanitize = rule => rule.replace(/\/style/gi, '\\/style') +const cache = {} + +/** + * computeId + * + * Compute and memoize a jsx id from a basedId and optionally props. + */ +export function computeId(baseId, props) { + if (!props) { + return `jsx-${baseId}` + } + + const propsToString = String(props) + const key = baseId + propsToString + + if (!cache[key]) { + cache[key] = `jsx-${hashString(`${baseId}-${propsToString}`)}` + } + + return cache[key] +} + +/** + * computeSelector + * + * Compute and memoize dynamic selectors. + */ +export function computeSelector(id, css) { + const selectoPlaceholderRegexp = /__jsx-style-dynamic-selector/g + // Sanitize SSR-ed CSS. + // Client side code doesn't need to be sanitized since we use + // document.createTextNode (dev) and the CSSOM api sheet.insertRule (prod). + if (typeof window === 'undefined') { + css = sanitize(css) + } + + const idcss = id + css + if (!cache[idcss]) { + cache[idcss] = css.replace(selectoPlaceholderRegexp, id) + } + + return cache[idcss] +} diff --git a/src/server.js b/src/server.js deleted file mode 100644 index 19e833f2..00000000 --- a/src/server.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' -import { flush } from './style' - -export default function flushToReact(options = {}) { - return flush().map(args => { - const id = args[0] - const css = args[1] - return React.createElement('style', { - id: `__${id}`, - // Avoid warnings upon render with a key - key: `__${id}`, - nonce: options.nonce ? options.nonce : undefined, - dangerouslySetInnerHTML: { - __html: css - } - }) - }) -} - -export function flushToHTML(options = {}) { - return flush().reduce((html, args) => { - const id = args[0] - const css = args[1] - html += `` - return html - }, '') -} diff --git a/src/style.js b/src/style.js index 3a08f433..0a4f916d 100644 --- a/src/style.js +++ b/src/style.js @@ -1,17 +1,16 @@ -import { useLayoutEffect } from 'react' -import StyleSheetRegistry from './stylesheet-registry' - -const styleSheetRegistry = new StyleSheetRegistry() - +import { useLayoutEffect, useContext } from 'react' +import { StyleSheetContext } from './stylesheet-registry' +import { computeId } from './lib/hash' export default function JSXStyle(props) { + const registry = useContext(StyleSheetContext) if (typeof window === 'undefined') { - styleSheetRegistry.add(props) + registry.add(props) return null } useLayoutEffect(() => { - styleSheetRegistry.add(props) + registry.add(props) return () => { - styleSheetRegistry.remove(props) + registry.remove(props) } // props.children can be string[], will be striped since id is identical }, [props.id, String(props.dynamic)]) @@ -23,13 +22,7 @@ JSXStyle.dynamic = info => { .map(tagInfo => { const baseId = tagInfo[0] const props = tagInfo[1] - return styleSheetRegistry.computeId(baseId, props) + return computeId(baseId, props) }) .join(' ') } - -export function flush() { - const cssRules = styleSheetRegistry.cssRules() - styleSheetRegistry.flush() - return cssRules -} diff --git a/src/stylesheet-registry.js b/src/stylesheet-registry.js index a3e2593f..d887265d 100644 --- a/src/stylesheet-registry.js +++ b/src/stylesheet-registry.js @@ -1,8 +1,24 @@ -import hashString from 'string-hash' -import DefaultStyleSheet from './lib/stylesheet' +import React, { useState, useContext, createContext } from 'react' -const sanitize = rule => rule.replace(/\/style/gi, '\\/style') -export default class StyleSheetRegistry { +import DefaultStyleSheet from './lib/stylesheet' +import { computeId, computeSelector } from './lib/hash' + +function mapRulesToStyle(cssRules, options = {}) { + return cssRules.map(args => { + const id = args[0] + const css = args[1] + return React.createElement('style', { + id: `__${id}`, + // Avoid warnings upon render with a key + key: `__${id}`, + nonce: options.nonce ? options.nonce : undefined, + dangerouslySetInnerHTML: { + __html: css + } + }) + }) +} +export class StyleSheetRegistry { constructor({ styleSheet = null, optimizeForSpeed = false, @@ -26,9 +42,6 @@ export default class StyleSheetRegistry { this._fromServer = undefined this._indices = {} this._instancesCounts = {} - - this.computeId = this.createComputeId() - this.computeSelector = this.createComputeSelector() } add(props) { @@ -99,9 +112,6 @@ export default class StyleSheetRegistry { this._fromServer = undefined this._indices = {} this._instancesCounts = {} - - this.computeId = this.createComputeId() - this.computeSelector = this.createComputeSelector() } cssRules() { @@ -126,70 +136,25 @@ export default class StyleSheetRegistry { ) } - /** - * createComputeId - * - * Creates a function to compute and memoize a jsx id from a basedId and optionally props. - */ - createComputeId() { - const cache = {} - return function(baseId, props) { - if (!props) { - return `jsx-${baseId}` - } - - const propsToString = String(props) - const key = baseId + propsToString - // return `jsx-${hashString(`${baseId}-${propsToString}`)}` - if (!cache[key]) { - cache[key] = `jsx-${hashString(`${baseId}-${propsToString}`)}` - } - - return cache[key] - } - } - - /** - * createComputeSelector - * - * Creates a function to compute and memoize dynamic selectors. - */ - createComputeSelector( - selectoPlaceholderRegexp = /__jsx-style-dynamic-selector/g - ) { - const cache = {} - return function(id, css) { - // Sanitize SSR-ed CSS. - // Client side code doesn't need to be sanitized since we use - // document.createTextNode (dev) and the CSSOM api sheet.insertRule (prod). - if (!this._isBrowser) { - css = sanitize(css) - } - - const idcss = id + css - if (!cache[idcss]) { - cache[idcss] = css.replace(selectoPlaceholderRegexp, id) - } - - return cache[idcss] - } + styles(options) { + return mapRulesToStyle(this.cssRules(), options) } getIdAndRules(props) { const { children: css, dynamic, id } = props if (dynamic) { - const styleId = this.computeId(id, dynamic) + const styleId = computeId(id, dynamic) return { styleId, rules: Array.isArray(css) - ? css.map(rule => this.computeSelector(styleId, rule)) - : [this.computeSelector(styleId, css)] + ? css.map(rule => computeSelector(styleId, rule)) + : [computeSelector(styleId, css)] } } return { - styleId: this.computeId(id), + styleId: computeId(id), rules: Array.isArray(css) ? css : [css] } } @@ -217,3 +182,26 @@ function invariant(condition, message) { throw new Error(`StyleSheetRegistry: ${message}.`) } } + +export const StyleSheetContext = createContext(null) + +export function createStyleRegistry() { + return new StyleSheetRegistry() +} + +export function StyleRegistry({ registry: configuredRegistry, children }) { + const rootRegistry = useContext(StyleSheetContext) + const [registry] = useState( + () => rootRegistry || configuredRegistry || createStyleRegistry() + ) + + return React.createElement( + StyleSheetContext.Provider, + { value: registry }, + children + ) +} + +export function useStyleRegistry() { + return useContext(StyleSheetContext) +} diff --git a/test/index.js b/test/index.js index d0dfc89c..3a14511c 100644 --- a/test/index.js +++ b/test/index.js @@ -5,8 +5,49 @@ import ReactDOM from 'react-dom/server' // Ours import plugin from '../src/babel' +import JSXStyle from '../src/style' +import { + StyleRegistry, + useStyleRegistry, + createStyleRegistry +} from '../src/stylesheet-registry' import _transform, { transformSource as _transformSource } from './_transform' +const flushToHTML = (registry, options = {}) => { + const cssRules = registry.cssRules() + registry.flush() + return cssRules.reduce((html, args) => { + const id = args[0] + const css = args[1] + html += `` + return html + }, '') +} + +function mapCssRulesToReact(cssRules, options = {}) { + return cssRules.map(args => { + const id = args[0] + const css = args[1] + return React.createElement('style', { + id: `__${id}`, + // Avoid warnings upon render with a key + key: `__${id}`, + nonce: options.nonce ? options.nonce : undefined, + dangerouslySetInnerHTML: { + __html: css + } + }) + }) +} + +function flushToReact(registry, options = {}) { + const cssRules = registry.cssRules() + registry.flush() + return mapCssRulesToReact(cssRules, options) +} + const transform = (file, opts = {}) => _transform(file, { plugins: [plugin], @@ -143,18 +184,46 @@ test('works with exported non-jsx style (CommonJS modules)', async t => { t.snapshot(code) }) -function clearModulesCache() { - ;['../src/lib/stylesheet', '../src/style', '../src/server'].forEach( - moduleName => { - delete require.cache[require.resolve(moduleName)] - } - ) -} +test('sever rendering with hook api', t => { + const registry = createStyleRegistry() + function Head() { + const registry = useStyleRegistry() + const styles = registry.styles() + registry.flush() + // should be empty and `push` won't effect styles + const stylesAfterFlushed = registry.styles() + styles.push(...stylesAfterFlushed) + return React.createElement('head', null, styles) + } + + function App() { + const color = 'green' + return React.createElement( + 'div', + null, + React.createElement(Head), + React.createElement(JSXStyle, { id: 2 }, 'div { color: blue }'), + React.createElement(JSXStyle, { id: 3 }, `div { color: ${color} }`) + ) + } + + // Expected DOM string + const styles = + '' + + '' + + const expected = `${styles}` + + const createContextualApp = type => + React.createElement(StyleRegistry, { registry }, React.createElement(type)) + + // Render using react + ReactDOM.renderToString(createContextualApp(App)) + const html = ReactDOM.renderToStaticMarkup(createContextualApp(Head)) + t.is(html, expected) +}) test('server rendering', t => { - clearModulesCache() - const JSXStyle = require('../src/style').default - const { default: flush, flushToHTML } = require('../src/server') function App() { const color = 'green' return React.createElement( @@ -190,31 +259,32 @@ test('server rendering', t => { '' + '' + const registry = createStyleRegistry() + const createApp = () => + React.createElement(StyleRegistry, { registry }, React.createElement(App)) + // Render using react - ReactDOM.renderToString(React.createElement(App)) + ReactDOM.renderToString(createApp()) const html = ReactDOM.renderToStaticMarkup( - React.createElement('head', null, flush()) + React.createElement('head', null, flushToReact(registry)) ) t.is(html, `${expected}`) // Assert that memory is empty - t.is(0, flush().length) - t.is('', flushToHTML()) + t.is(0, registry.cssRules().length) + t.is('', flushToHTML(registry)) // Render to html again - ReactDOM.renderToString(React.createElement(App)) - t.is(expected, flushToHTML()) + ReactDOM.renderToString(createApp()) + t.is(expected, flushToHTML(registry)) // Assert that memory is empty - t.is(0, flush().length) - t.is('', flushToHTML()) + t.is(0, flushToReact(registry).length) + t.is('', flushToHTML(registry)) }) test('server rendering with nonce', t => { - clearModulesCache() - const JSXStyle = require('../src/style').default - const { default: flush, flushToHTML } = require('../src/server') function App() { const color = 'green' return React.createElement( @@ -244,6 +314,10 @@ test('server rendering with nonce', t => { ) } + const registry = createStyleRegistry() + const createApp = () => + React.createElement(StyleRegistry, { registry }, React.createElement(App)) + // Expected CSS const expected = '' + @@ -251,30 +325,31 @@ test('server rendering with nonce', t => { '' // Render using react - ReactDOM.renderToString(React.createElement(App)) + ReactDOM.renderToString(createApp()) const html = ReactDOM.renderToStaticMarkup( - React.createElement('head', null, flush({ nonce: 'test-nonce' })) + React.createElement( + 'head', + null, + flushToReact(registry, { nonce: 'test-nonce' }) + ) ) t.is(html, `${expected}`) // Assert that memory is empty - t.is(0, flush({ nonce: 'test-nonce' }).length) - t.is('', flushToHTML({ nonce: 'test-nonce' })) + t.is(0, flushToReact(registry, { nonce: 'test-nonce' }).length) + t.is('', flushToHTML(registry, { nonce: 'test-nonce' })) // Render to html again - ReactDOM.renderToString(React.createElement(App)) - t.is(expected, flushToHTML({ nonce: 'test-nonce' })) + ReactDOM.renderToString(createApp()) + t.is(expected, flushToHTML(registry, { nonce: 'test-nonce' })) // Assert that memory is empty - t.is(0, flush({ nonce: 'test-nonce' }).length) - t.is('', flushToHTML({ nonce: 'test-nonce' })) + t.is(0, flushToReact(registry, { nonce: 'test-nonce' }).length) + t.is('', flushToHTML(registry, { nonce: 'test-nonce' })) }) test('optimized styles do not contain new lines', t => { - clearModulesCache() - const JSXStyle = require('../src/style').default - const { default: flush } = require('../src/server') function App() { return React.createElement( 'div', @@ -289,9 +364,13 @@ test('optimized styles do not contain new lines', t => { ) } - ReactDOM.renderToString(React.createElement(App)) + const registry = createStyleRegistry() + const createApp = () => + React.createElement(StyleRegistry, { registry }, React.createElement(App)) + + ReactDOM.renderToString(createApp()) const html = ReactDOM.renderToStaticMarkup( - React.createElement('head', null, flush()) + React.createElement('head', null, flushToReact(registry)) ) const expected = '' diff --git a/test/snapshots/external.js.snap b/test/snapshots/external.js.snap index b4ab0dfd..e69de731 100644 Binary files a/test/snapshots/external.js.snap and b/test/snapshots/external.js.snap differ diff --git a/test/snapshots/index.js.md b/test/snapshots/index.js.md index eccd6cdc..93741de0 100644 --- a/test/snapshots/index.js.md +++ b/test/snapshots/index.js.md @@ -225,7 +225,7 @@ Generated by [AVA](https://ava.li). ␊ };` -## works with existing identifier for \_JSXStyle +## works with existing identifier for _JSXStyle > Snapshot 1 diff --git a/test/snapshots/index.js.snap b/test/snapshots/index.js.snap index 3139b1ad..7fe155a0 100644 Binary files a/test/snapshots/index.js.snap and b/test/snapshots/index.js.snap differ diff --git a/test/snapshots/plugins.js.snap b/test/snapshots/plugins.js.snap index dd9e66af..3ddf9d22 100644 Binary files a/test/snapshots/plugins.js.snap and b/test/snapshots/plugins.js.snap differ diff --git a/test/stylesheet-registry.js b/test/stylesheet-registry.js index 12c06840..3c9b3b50 100644 --- a/test/stylesheet-registry.js +++ b/test/stylesheet-registry.js @@ -2,9 +2,10 @@ import test from 'ava' // Ours -import StyleSheetRegistry from '../src/stylesheet-registry' +import { StyleSheetRegistry } from '../src/stylesheet-registry' 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 }) { const registry = new StyleSheetRegistry({ @@ -254,9 +255,6 @@ test( test( 'createComputeId', withMock(withMockDocument, t => { - const utilRegistry = makeRegistry() - const computeId = utilRegistry.createComputeId() - // without props t.is(computeId('123'), 'jsx-123') @@ -270,11 +268,6 @@ test( test( 'createComputeSelector', withMock(withMockDocument, t => { - const utilRegistry = makeRegistry() - const computeSelector = utilRegistry - .createComputeSelector() - .bind(utilRegistry) - t.is( computeSelector( 'jsx-123',