Repository: vercel/styled-jsx Branch: main Commit: d7a59379134d Files: 113 Total size: 244.9 KB Directory structure: gitextract_mv38yaut/ ├── .eslintignore ├── .github/ │ ├── ISSUE_TEMPLATE.md │ └── workflows/ │ ├── main.yml │ └── prs.yml ├── .gitignore ├── .npmrc ├── .prettierrc.json ├── CODEOWNERS ├── Changelog.md ├── babel-test.js ├── babel.js ├── css.d.ts ├── css.js ├── global.d.ts ├── index.d.ts ├── index.js ├── lib/ │ ├── style-transform.js │ └── stylesheet.js ├── license.md ├── macro.d.ts ├── macro.js ├── package.json ├── readme.md ├── src/ │ ├── .babelrc │ ├── _constants.js │ ├── _utils.js │ ├── babel-external.js │ ├── babel-test.js │ ├── babel.js │ ├── index.js │ ├── lib/ │ │ ├── hash.js │ │ ├── style-transform.js │ │ └── stylesheet.js │ ├── macro.js │ ├── style.js │ ├── stylesheet-registry.js │ └── webpack.js ├── style.d.ts ├── style.js ├── test/ │ ├── .babelrc │ ├── __snapshots__/ │ │ ├── attribute.js.snap │ │ ├── external.js.snap │ │ ├── index.js.snap │ │ ├── macro.js.snap │ │ ├── plugins.js.snap │ │ └── styles.js.snap │ ├── _read.js │ ├── _transform.js │ ├── attribute.js │ ├── external.js │ ├── fixtures/ │ │ ├── absent.js │ │ ├── attribute-generation-classname-rewriting.js │ │ ├── attribute-generation-modes.js │ │ ├── class.js │ │ ├── component-attribute.js │ │ ├── conflicts.js │ │ ├── css-tag-same-file.js │ │ ├── different-jsx-ids.js │ │ ├── dynamic-element-class.js │ │ ├── dynamic-element-external.js │ │ ├── dynamic-element.js │ │ ├── dynamic-this-value-in-arrow.js │ │ ├── expressions.js │ │ ├── external-stylesheet-global.js │ │ ├── external-stylesheet-multi-line.js │ │ ├── external-stylesheet.js │ │ ├── fragment.js │ │ ├── global.js │ │ ├── ignore.js │ │ ├── macro.js │ │ ├── mixed-global-scoped.js │ │ ├── multiple-jsx.js │ │ ├── nested-style-tags.js │ │ ├── non-styled-jsx-style.js │ │ ├── not-styled-jsx-tagged-templates.js │ │ ├── plugins/ │ │ │ ├── another-plugin.js │ │ │ ├── invalid-plugin.js │ │ │ ├── multiple-options.js │ │ │ ├── options.js │ │ │ └── plugin.js │ │ ├── simple-fragment.js │ │ ├── source-maps.js │ │ ├── stateless.js │ │ ├── styles-external-invalid.js │ │ ├── styles-external-invalid2.js │ │ ├── styles.js │ │ ├── styles2.js │ │ ├── transform.css │ │ ├── whitespace.js │ │ └── with-plugins.js │ ├── helpers/ │ │ ├── babel-test.macro.js │ │ └── with-mock.js │ ├── index.js │ ├── index.ts │ ├── macro.js │ ├── plugins.js │ ├── snapshots/ │ │ ├── attribute.js.md │ │ ├── attribute.js.snap │ │ ├── external.js.md │ │ ├── external.js.snap │ │ ├── index.js.md │ │ ├── index.js.snap │ │ ├── macro.js.md │ │ ├── macro.js.snap │ │ ├── plugins.js.md │ │ ├── plugins.js.snap │ │ ├── styles.js.md │ │ └── styles.js.snap │ ├── styles.js │ ├── stylesheet-registry.js │ └── stylesheet.js ├── tsconfig.json └── webpack.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ test/fixtures ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ #### Do you want to request a _feature_ or report a _bug_? #### What is the current behavior? #### If the current behavior is a bug, please provide the steps to reproduce and possibly a minimal demo or testcase in the form of a Next.js app, CodeSandbox URL or similar #### What is the expected behavior? #### Environment (include versions) - Version of styled-jsx (or next.js if it's being used): - Browser: - OS: #### Did this work in previous versions? ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: push: branches: - main - alpha - beta tags: - '!*' pull_request: jobs: build: runs-on: ubuntu-latest steps: - name: Begin CI... uses: actions/checkout@v4 - name: Use Node 20 uses: actions/setup-node@v4 with: node-version: 20.x - name: Install dependencies run: yarn install --frozen-lockfile env: CI: true - name: Build run: yarn build env: CI: true - name: Lint run: yarn lint env: CI: true - name: Test run: yarn test env: CI: true - name: Test types run: yarn test-types env: CI: true - name: Release if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/alpha' || github.ref == 'refs/heads/beta') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} run: yarn semantic-release ================================================ FILE: .github/workflows/prs.yml ================================================ name: 'Lint PR' on: pull_request_target: types: - opened - edited - synchronize jobs: main: runs-on: ubuntu-latest steps: - uses: amannn/action-semantic-pull-request@v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ # dependencies node_modules # build output dist out # logs npm-debug.log ================================================ FILE: .npmrc ================================================ package-lock=false save-exact = true ================================================ FILE: .prettierrc.json ================================================ { "singleQuote": true, "semi": false } ================================================ FILE: CODEOWNERS ================================================ # optionally request a review from @rauchg, @nkzawa, @leo manually * @huozhi * @ijjk ================================================ FILE: Changelog.md ================================================ # Changelog ## [5.0.0] ### Features - Introduce contextual styles (#744) - Opt-in react 18 insertion effect hook when available (#753) - Fallback to module level registry in browser (#768) ### Improvements - Make JSXStyle return a noop if the registry context is not provided (#749) - Fix typings of `nonce` property - Pre-compile dependencies to reduce install size/time (#770) ### BREAKING CHANGES #### APIs - `styled-jsx/server` import path is deprecated - `flush` and `flushToHTML` from `styled-jsx/server` APIs are deprecated - New component `` is introduced - New APIs `useStyleRegistry` and `createStyleRegistry` are introduced #### Usage If you're only using styled-jsx purely client side, nothing will effect you. If you're using styled-jsx inside Next.js without customization, Next.js will automatically handle the changes for you. If you have your own customization with styled-jsx in Next.js, for example you have a custom `_document`: By default, doing this will let Next.js collect styles and pass them down. ```jsx class Document extends React.Component { static async getInitialProps(ctx) { return await ctx.defaultGetInitialProps(ctx) } } ``` Or for instance you're passing `nonce` property in `getInitialProps` of `_document`, this will let you configure it: ```diff class Document extends React.Component { static async getInitialProps(ctx) { - return await ctx.defaultGetInitialProps(ctx) + return await ctx.defaultGetInitialProps(ctx, { nonce }) } } ``` If you're building the SSR solution yourself with other frameworks, please checkout the **Server-Side Rendering** section in readme. ## [4.0.1] - Mark `@babel/core` as optional peer dependency ## [4.0.0] - Use react hooks to manage styles injection (#720) ### BREAKING CHANGES - Drop support for react versions < 16.8.0 ### Improvements - Drop babel 6 support (#730) - Auto publish alpha/beta versions ## [3.4.x] ### Improvements - Typing support - Inject unique \_JSXStyle identifier - Hide webpack loader warnings - Refactor the import helpers ================================================ FILE: babel-test.js ================================================ /* eslint-ignore */ module.exports = require('./dist/babel').test() ================================================ FILE: babel.js ================================================ module.exports = require('./dist/babel').default ================================================ FILE: css.d.ts ================================================ // Definitions by: @types/styled-jsx declare module 'styled-jsx/css' { import type { JSX } from "react"; function css(chunks: TemplateStringsArray, ...args: any[]): JSX.Element namespace css { export function global( chunks: TemplateStringsArray, ...args: any[] ): JSX.Element export function resolve( chunks: TemplateStringsArray, ...args: any[] ): { className: string; styles: JSX.Element } } export = css } ================================================ FILE: css.js ================================================ function notTranspiledError(name) { throw new Error( 'styled-jsx/css: if you are getting this error it means that your `' + name + '` tagged template literals were not transpiled.' ) } function css() { notTranspiledError('css') } css.global = function() { notTranspiledError('global') } css.resolve = function() { notTranspiledError('resolve') } module.exports = css module.exports.global = css.global module.exports.resolve = css.resolve ================================================ FILE: global.d.ts ================================================ import React from 'react' declare module 'react' { interface StyleHTMLAttributes extends HTMLAttributes { jsx?: boolean global?: boolean } } ================================================ FILE: index.d.ts ================================================ /// /// /// /// declare module 'styled-jsx' { import type { JSX } from "react"; export type StyledJsxStyleRegistry = { styles(options?: { nonce?: string }): JSX.Element[] flush(): void add(props: any): void remove(props: any): void } export function useStyleRegistry(): StyledJsxStyleRegistry export function StyleRegistry({ children, registry }: { children: JSX.Element | import('react').ReactNode registry?: StyledJsxStyleRegistry }): JSX.Element export function createStyleRegistry(): StyledJsxStyleRegistry } ================================================ FILE: index.js ================================================ module.exports = require('./dist/index') ================================================ FILE: lib/style-transform.js ================================================ /* eslint-ignore */ module.exports = require('../dist/lib/style-transform') ================================================ FILE: lib/stylesheet.js ================================================ module.exports = require('../dist/lib/stylesheet') ================================================ FILE: license.md ================================================ MIT License Copyright (c) 2016-present Vercel, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: macro.d.ts ================================================ declare module 'styled-jsx/macro' { import type { JSX } from "react"; namespace macro { function resolve( chunks: TemplateStringsArray, ...args: any[] ): { className: string styles: JSX.Element } } export = macro } ================================================ FILE: macro.js ================================================ module.exports = require('./dist/babel').macro() ================================================ FILE: package.json ================================================ { "name": "styled-jsx", "version": "0.0.0-development", "license": "MIT", "repository": "vercel/styled-jsx", "description": "Full CSS support for JSX without compromises", "files": [ "dist", "lib", "global.d.ts", "index.d.ts", "index.js", "babel.js", "babel-test.js", "style.js", "style.d.ts", "macro.js", "macro.d.ts", "css.js", "css.d.ts", "webpack.js", "license.md" ], "typings": "./index.d.ts", "scripts": { "build-babel": "bunchee src/babel.js -f cjs -e babel-plugin-macros --runtime node -o dist/babel/index.js", "build": "rm -rf dist && rm -rf out && yarn build-webpack && yarn build-index && yarn build-babel", "build-webpack": "bunchee src/webpack.js -f cjs --runtime node -o dist/webpack/index.js", "build-index": "bunchee src/index.js -f cjs --runtime node -o dist/index/index.js", "test": "ava", "test-types": "tsc --project tsconfig.json --noEmit", "lint": "eslint ./src", "format": "prettier --write \"./{src,test}/**/*.{js,css}\"", "prepublishOnly": "yarn build && yarn test && yarn lint --quiet" }, "husky": { "hooks": { "pre-commit": "pretty-quick --staged" } }, "ava": { "require": [ "@babel/register" ] }, "eslintConfig": { "env": { "node": true, "browser": true, "es6": true }, "extends": [ "eslint:recommended", "prettier" ], "parserOptions": { "ecmaVersion": 11, "sourceType": "module" }, "rules": { "no-empty": 0, "capitalized-comments": 0, "valid-jsdoc": 0, "prefer-destructuring": 0, "padding-line-between-statements": 0 } }, "devDependencies": { "@babel/cli": "7.18.10", "@babel/core": "7.12.3", "@babel/plugin-proposal-object-rest-spread": "7.12.1", "@babel/plugin-syntax-jsx": "7.14.5", "@babel/plugin-transform-arrow-functions": "7.12.1", "@babel/plugin-transform-modules-commonjs": "7.12.1", "@babel/plugin-transform-runtime": "7.12.1", "@babel/preset-env": "7.12.1", "@babel/preset-react": "7.12.5", "@babel/register": "7.12.1", "@babel/runtime": "7.12.5", "@babel/types": "7.15.0", "@types/react": "18.3.3", "ava": "4.3.1", "babel-plugin-macros": "2.8.0", "bunchee": "2.1.5", "convert-source-map": "1.7.0", "eslint": "7.32.0", "eslint-config-prettier": "4.0.0", "husky": "4.3.0", "loader-utils": "1.4.2", "prettier": "1.16.4", "pretty-quick": "3.1.0", "react": "17.0.1", "react-dom": "17.0.1", "semantic-release": "17.2.2", "source-map": "0.7.3", "string-hash": "1.1.3", "stylis": "3.5.4", "stylis-rule-sheet": "0.0.10", "typescript": "~5.0.0" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { "optional": true }, "babel-plugin-macros": { "optional": true } }, "release": { "branches": [ "main", "alpha", "beta" ] }, "engines": { "node": ">= 12.0.0" }, "keywords": [ "babel-plugin-macros", "vercel", "zeit", "css-in-js", "css" ], "dependencies": { "client-only": "0.0.1" }, "packageManager": "yarn@1.22.22" } ================================================ FILE: readme.md ================================================ # styled-jsx [![build status](https://github.com/vercel/styled-jsx/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/vercel/styled-jsx/actions?query=branch%3Amain) Full, scoped and component-friendly CSS support for JSX (rendered on the server or the client). Code and docs are for v3 which we highly recommend you to try. Looking for styled-jsx v2? Switch to the [v2 branch](https://github.com/vercel/styled-jsx/tree/v2). - [Getting started](#getting-started) - [Configuration options](#configuration-options) - [`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) - [Targeting The Root](#targeting-the-root) - [Global styles](#global-styles) - [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) - [Constants](#constants) - [Server-Side Rendering](#server-side-rendering) - [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) - [CSS Preprocessing via Plugins](#css-preprocessing-via-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/vercel/styled-jsx/issues/319#issuecomment-349239326) - [Build a component library with styled-jsx](#build-a-component-library-with-styled-jsx) - [Syntax Highlighting](#syntax-highlighting) - [ESLint](#eslint) - [TypeScript](#typescript) - [Credits](#credits) ## Getting started Firstly, install the package: ```bash npm install --save styled-jsx ``` Next, add `styled-jsx/babel` to `plugins` in your babel configuration: ```json { "plugins": ["styled-jsx/babel"] } ``` Now add ` ) ``` ## Configuration options The following are optional settings for the babel plugin. #### `optimizeForSpeed` Blazing fast and optimized CSS rules injection system based on the CSSOM APIs. ```json { "plugins": [["styled-jsx/babel", { "optimizeForSpeed": true }]] } ``` When in production\* this mode is automatically enabled.
Beware that when using this option source maps cannot be generated and styles cannot be edited via the devtools. \* `process.env.NODE_ENV === 'production'` #### `sourceMaps` Generates source maps (default: `false`) #### `styleModule` Module that the transpiled files should import (default: `styled-jsx/style`) #### `vendorPrefixes` Turn on/off automatic vendor prefixing (default: `true`) ## Features - Full CSS support, no tradeoffs in power - Runtime size of just **3kb** (gzipped, from 12kb) - Complete isolation: Selectors, animations, keyframes - Built-in CSS vendor prefixing - Very fast, minimal and efficient transpilation (see below) - High-performance runtime-CSS-injection when not server-rendering - Future-proof: Equivalent to server-renderable "Shadow CSS" - Source maps support - Dynamic styles and themes support - CSS Preprocessing via Plugins ## Using in Next.js Next.js automatically configures `styled-jsx` with babel or swc, you don't have to configure it manually. ## How It Works The example above transpiles to the following: ```jsx import _JSXStyle from 'styled-jsx/style' export default () => (

only this paragraph will get the style :)

<_JSXStyle id="123">{`p.jsx-123 {color: red;}`}
) ``` ### Why It Works Like This Unique classnames give us style encapsulation and `_JSXStyle` is heavily optimized for: - Injecting styles upon render - Only injecting a certain component's style once (even if the component is included multiple times) - Removing unused styles - Keeping track of styles for server-side rendering ### Targeting The Root Notice that the outer `
` from the example above also gets a `jsx-123` classname. We do this so that you can target the "root" element, in the same manner that [`:host`](https://web.dev/articles/shadowdom-v1) works with Shadow DOM. If you want to target _only_ the host, we suggest you use a class: ```jsx export default () => (
) ``` ### Global styles To skip scoping entirely, you can make the global-ness of your styles explicit by adding _global_. ```jsx export default () => (
) ``` The advantage of using this over `
) ``` ### Dynamic styles To make a component's visual representation customizable from the outside world there are three options. #### Via interpolated dynamic props 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 => ( ) ``` 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 => ( ) ``` #### Via `className` toggling The second option is to pass properties that toggle class names. ```jsx const Button = props => ( ) ``` Then you would use this component as either `` or ``. #### Via inline `style` \***best for animations** Imagine that you wanted to make the padding in the button above completely customizable. You can override the CSS you configure via inline-styles: ```jsx const Button = ({ padding, children }) => ( ) ``` In this example, the padding defaults to the one set in ` ) ``` Please keep in mind that constants defined outside of the component scope are treated as static styles. ## Server-Side Rendering `styled-jsx` v5 introduced `StyledRegistry` component and `useStyleRegistry` hook to let you scope styles rendering in each SSR render to keep concurrent-safe. - `registry.styles()` will return the array of react components for style tags. - `registry.flush()` can clean the existing styles in the registry, it's optional for SSR when you have a standalone registry for each SSR render. > Next.js 12 integrates with `styled-jsx` v5 and manages the registry for you. ```jsx import React from 'react' import ReactDOM from 'react-dom/server' import { StyleRegistry, useStyleRegistry } from 'styled-jsx' import App from './app' function Styles() { const registry = useStyleRegistry() const styles = registry.styles() return <>{styles} } export default (req, res) => { const app = ReactDOM.renderToString() const html = ReactDOM.renderToStaticMarkup(
) res.end('' + html) } ``` There's also a new API `createStyleRegistry` that is introduced when you have to create a registry manually. In this way you can operate the registry yourself to extract the rendered styles (`registry.styles()`) or flush them out (`registry.flush()`). ```js const registry = createStyleRegistry() const styles = registry.styles() // access styles function Page() { return ( ) } ``` By default `` will use the `registry` from root top `StyleRegistry`, which means there's only one `registry` in the react tree. It's **paramount** that you use one of these two functions so that the generated styles can be diffed when the client loads and duplicate styles are avoided. ### Content Security Policy 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 `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';` ### External CSS and styles outside of the component 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. #### External styles In an external file: ```js /* styles.js */ import css from 'styled-jsx/css' // Scoped styles export const button = css` button { color: hotpink; } ` // Global styles export const body = css.global`body { margin: 0; }` // Resolved styles export const link = css.resolve`a { color: green; }` // link.className -> scoped className to apply to `a` elements e.g. jsx-123 // link.styles -> styles element to render inside of your component // Works also with default exports export default css` div { color: green; } ` ``` You can then import and use those styles: ```jsx import styles, { button, body } from './styles' export default () => (
) ``` N.B. All the tags except for [`resolve`](#the-resolve-tag) don't support dynamic styles. `resolve` and `global` can also be imported individually: ```js import { resolve } from 'styled-jsx/css' import { global } from 'styled-jsx/css' ``` If you use Prettier we recommend you to use the default `css` export syntax since the tool doesn't support named imports. #### Styles outside of components The `css` tag from `styled-jsx/css` can be also used to define styles in your components files but outside of the component itself. This might help with keeping `render` methods smaller. ```jsx import css from 'styled-jsx/css' export default () => (
) 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 `
) ``` To consume the styles in your component you can import them from your CSS file and render them using a ` ) ``` ### Styling third parties / child components from the parent When the component accepts a `className` (or ad-hoc) prop as a way to allow customizations then you can use [the `resolve` tag from `styled-jsx/css`](#the-resolve-tag). When the component doesn't accept any `className` or doesn't expose any API to customize the component, then your only option is to use `:global()` styles: ```jsx export default () => (
) ``` Please keep in mind that `:global()` styles will affect the entire subtree, so in many cases you may want to be careful and use the children (direct descendant) selector `>`. ### Build a component library with styled-jsx There's an [article](https://medium.com/@tomaszmularczyk89/guide-to-building-a-react-components-library-with-rollup-and-styled-jsx-694ec66bd2) explaining how to bundle React components with Rollup and styled-jsx as an external dependency. ## Syntax Highlighting When working with template literals a common drawback is missing syntax highlighting. The following editors currently have support for highlighting CSS inside ` ) ``` ### 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. ```json { "html": { "snippets": { "style-jsx": "" } } } ``` ### 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 ``` ### Vim 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: ``` "settings": {"import/core-modules": ["styled-jsx/css"] } ``` ## TypeScript If you're using TypeScript, then in order to allow ` `) } } export const getJSXStyleInfo = (expr, scope) => { const { node } = expr const location = node.loc // Assume string literal if (t.isStringLiteral(node)) { return { hash: hashString(node.value), css: node.value, expressions: [], dynamic: false, location } } // Simple template literal without expressions if (node.expressions.length === 0) { return { hash: hashString(node.quasis[0].value.raw), css: node.quasis[0].value.raw, expressions: [], dynamic: false, location } } // Special treatment for template literals that contain expressions: // // Expressions are replaced with a placeholder // so that the CSS compiler can parse and // transform the css source string // without having to know about js literal expressions. // Later expressions are restored. // // e.g. // p { color: ${myConstant}; } // becomes // p { color: %%styled-jsx-placeholder-${id}%%; } const { quasis, expressions } = node const hash = hashString(expr.getSource().slice(1, -1)) let dynamic = Boolean(scope) if (dynamic) { try { const val = expr.evaluate() if (val.confident) { dynamic = false } else if (val.deopt) { const computedObject = val.deopt .get('object') .resolve() .evaluate() dynamic = !computedObject.confident } } catch (_) {} } const css = quasis.reduce( (css, quasi, index) => `${css}${quasi.value.raw}${ quasis.length === index + 1 ? '' : `%%styled-jsx-placeholder-${index}%%` }`, '' ) return { hash, css, expressions, dynamic, location } } export const computeClassNames = ( styles, externalJsxId, styleComponentImportName ) => { if (styles.length === 0) { return { className: externalJsxId } } const hashes = styles.reduce( (acc, styles) => { if (styles.dynamic === false) { acc.static.push(styles.hash) } else { acc.dynamic.push(styles) } return acc }, { static: [], dynamic: [] } ) const staticClassName = `jsx-${hashString(hashes.static.join(','))}` // Static and optionally external classes. E.g. // '[jsx-externalClasses] jsx-staticClasses' if (hashes.dynamic.length === 0) { return { staticClassName, className: externalJsxId ? concat(t.stringLiteral(staticClassName + ' '), externalJsxId) : t.stringLiteral(staticClassName) } } // _JSXStyle.dynamic([ ['1234', [props.foo, bar, fn(props)]], ... ]) const dynamic = t.callExpression( // Callee: _JSXStyle.dynamic t.memberExpression( t.identifier(styleComponentImportName), t.identifier(STYLE_COMPONENT_DYNAMIC) ), // Arguments [ t.arrayExpression( hashes.dynamic.map(styles => t.arrayExpression([ t.stringLiteral(hashString(styles.hash + staticClassName)), t.arrayExpression(styles.expressions) ]) ) ) ] ) // Dynamic and optionally external classes. E.g. // '[jsx-externalClasses] ' + _JSXStyle.dynamic([ ['1234', [props.foo, bar, fn(props)]], ... ]) if (hashes.static.length === 0) { return { staticClassName, className: externalJsxId ? concat(concat(externalJsxId, t.stringLiteral(' ')), dynamic) : dynamic } } // Static, dynamic and optionally external classes. E.g. // '[jsx-externalClasses] jsx-staticClasses ' + _JSXStyle.dynamic([ ['5678', [props.foo, bar, fn(props)]], ... ]) return { staticClassName, className: externalJsxId ? concat( concat(externalJsxId, t.stringLiteral(` ${staticClassName} `)), dynamic ) : concat(t.stringLiteral(`${staticClassName} `), dynamic) } } export const templateLiteralFromPreprocessedCss = (css, expressions) => { const quasis = [] const finalExpressions = [] const parts = css.split(/(?:%%styled-jsx-placeholder-(\d+)%%)/g) if (parts.length === 1) { return t.stringLiteral(css) } parts.forEach((part, index) => { if (index % 2 > 0) { // This is necessary because, after preprocessing, declarations might have been alterate. // eg. properties are auto prefixed and therefore expressions need to match. finalExpressions.push(expressions[part]) } else { quasis.push(part) } }) return t.templateLiteral( quasis.map((quasi, index) => t.templateElement( { raw: quasi, cooked: quasi }, quasis.length === index + 1 ) ), finalExpressions ) } export const cssToBabelType = css => { if (typeof css === 'string') { return t.stringLiteral(css) } if (Array.isArray(css)) { return t.arrayExpression(css) } return t.cloneDeep(css) } export const makeStyledJsxTag = ( id, transformedCss, expressions = [], styleComponentImportName ) => { const css = cssToBabelType(transformedCss) const attributes = [ t.jSXAttribute( t.jSXIdentifier(STYLE_COMPONENT_ID), t.jSXExpressionContainer( typeof id === 'string' ? t.stringLiteral(id) : id ) ) ] if (expressions.length > 0) { attributes.push( t.jSXAttribute( t.jSXIdentifier(STYLE_COMPONENT_DYNAMIC), t.jSXExpressionContainer(t.arrayExpression(expressions)) ) ) } return t.jSXElement( t.jSXOpeningElement(t.jSXIdentifier(styleComponentImportName), attributes), t.jSXClosingElement(t.jSXIdentifier(styleComponentImportName)), [t.jSXExpressionContainer(css)] ) } export const makeSourceMapGenerator = file => { const filename = file.sourceFileName const generator = new SourceMapGenerator({ file: filename, sourceRoot: file.sourceRoot }) generator.setSourceContent(filename, file.code) return generator } export const addSourceMaps = (code, generator, filename) => { const sourceMaps = [ convert.fromObject(generator).toComment({ multiline: true }), `/*@ sourceURL=${filename.replace(/\\/g, '\\\\')} */` ] if (Array.isArray(code)) { return code.concat(sourceMaps) } return [code].concat(sourceMaps).join('\n') } const combinedPluginsCache = { plugins: null, combined: null } export const combinePlugins = plugins => { if (!plugins) { return css => css } const pluginsToString = JSON.stringify(plugins) if (combinedPluginsCache.plugins === pluginsToString) { return combinedPluginsCache.combined } if ( !Array.isArray(plugins) || plugins.some(p => !Array.isArray(p) && typeof p !== 'string') ) { throw new Error( '`plugins` must be an array of plugins names (string) or an array `[plugin-name, {options}]`' ) } combinedPluginsCache.plugins = pluginsToString combinedPluginsCache.combined = plugins .map((plugin, i) => { let options = {} if (Array.isArray(plugin)) { options = plugin[1] || {} plugin = plugin[0] if (Object.prototype.hasOwnProperty.call(options, 'babel')) { throw new Error(` Error while trying to register the styled-jsx plugin: ${plugin} The option name \`babel\` is reserved. `) } } log('Loading plugin from path: ' + plugin) let p = require(plugin) if (p.default) { p = p.default } const type = typeof p if (type !== 'function') { throw new Error( `Expected plugin ${ plugins[i] } to be a function but instead got ${type}` ) } return { plugin: p, options } }) .reduce( (previous, { plugin, options }) => (css, babelOptions) => plugin(previous ? previous(css, babelOptions) : css, { ...options, babel: babelOptions }), null ) return combinedPluginsCache.combined } const getPrefix = (isDynamic, id) => isDynamic ? '.__jsx-style-dynamic-selector' : `.${id}` export const processCss = (stylesInfo, options) => { const { hash, css, expressions, dynamic, location, file, isGlobal, plugins, vendorPrefixes, sourceMaps } = stylesInfo const fileInfo = { code: file.code, sourceRoot: file.opts.sourceRoot, filename: file.opts.filename || file.filename } fileInfo.sourceFileName = file.opts.sourceFileName || file.sourceFileName || // According to https://babeljs.io/docs/en/options#source-map-options // filenameRelative = path.relative(file.opts.cwd, file.opts.filename) // sourceFileName = path.basename(filenameRelative) // or simply // sourceFileName = path.basename(file.opts.filename) (fileInfo.filename && path.basename(fileInfo.filename)) const staticClassName = stylesInfo.staticClassName || `jsx-${hashString(hash)}` const { splitRules } = options const useSourceMaps = Boolean(sourceMaps) && !splitRules const pluginsOptions = { location: { start: { ...location.start }, end: { ...location.end } }, vendorPrefixes, sourceMaps: useSourceMaps, isGlobal, filename: fileInfo.filename } let transformedCss if (useSourceMaps) { const generator = makeSourceMapGenerator(fileInfo) const filename = fileInfo.sourceFileName transformedCss = addSourceMaps( transform( isGlobal ? '' : getPrefix(dynamic, staticClassName), plugins(css, pluginsOptions), { generator, offset: location.start, filename, splitRules, vendorPrefixes } ), generator, filename ) } else { transformedCss = transform( isGlobal ? '' : getPrefix(dynamic, staticClassName), plugins(css, pluginsOptions), { splitRules, vendorPrefixes } ) } if (expressions.length > 0) { if (typeof transformedCss === 'string') { transformedCss = templateLiteralFromPreprocessedCss( transformedCss, expressions ) } else { transformedCss = transformedCss.map(transformedCss => templateLiteralFromPreprocessedCss(transformedCss, expressions) ) } } else if (Array.isArray(transformedCss)) { transformedCss = transformedCss.map(transformedCss => t.stringLiteral(transformedCss) ) } return { hash: dynamic ? hashString(hash + staticClassName) : hashString(hash), css: transformedCss, expressions: dynamic && expressions } } export const booleanOption = opts => { let ret opts.some(opt => { if (typeof opt === 'boolean') { ret = opt return true } return false }) return ret } export const createReactComponentImportDeclaration = state => { return t.importDeclaration( [t.importDefaultSpecifier(t.identifier(state.styleComponentImportName))], t.stringLiteral(state.styleModule) ) } export const setStateOptions = state => { const vendorPrefixes = booleanOption([ state.opts.vendorPrefixes, state.file.opts.vendorPrefixes ]) state.opts.vendorPrefixes = typeof vendorPrefixes === 'boolean' ? vendorPrefixes : true const sourceMaps = booleanOption([ state.opts.sourceMaps, state.file.opts.sourceMaps ]) state.opts.sourceMaps = Boolean(sourceMaps) if (!state.plugins) { state.plugins = combinePlugins(state.opts.plugins, { sourceMaps: state.opts.sourceMaps, vendorPrefixes: state.opts.vendorPrefixes }) } state.styleModule = typeof state.opts.styleModule === 'string' ? state.opts.styleModule : 'styled-jsx/style' } export function log(message) { console.log('[styled-jsx] ' + message) } ================================================ FILE: src/babel-external.js ================================================ import * as t from '@babel/types' import { getJSXStyleInfo, processCss, cssToBabelType, validateExternalExpressions, getScope, computeClassNames, makeStyledJsxTag, setStateOptions } from './_utils' const isModuleExports = t.buildMatchMemberExpression('module.exports') export function processTaggedTemplateExpression({ type, path, file, splitRules, plugins, vendorPrefixes, sourceMaps, styleComponentImportName }) { const templateLiteral = path.get('quasi') let scope // Check whether there are undefined references or // references to this.something (e.g. props or state). // We allow dynamic styles only when resolving styles. if (type !== 'resolve') { validateExternalExpressions(templateLiteral) } else if (!path.scope.path.isProgram()) { scope = getScope(path) } const stylesInfo = getJSXStyleInfo(templateLiteral, scope) const { staticClassName, className } = computeClassNames( [stylesInfo], undefined, styleComponentImportName ) const styles = processCss( { ...stylesInfo, staticClassName, file, isGlobal: type === 'global', plugins, vendorPrefixes, sourceMaps }, { splitRules } ) if (type === 'resolve') { const { hash, css, expressions } = styles path.replaceWith( // { // styles: <_JSXStyle ... />, // className: 'jsx-123' // } t.objectExpression([ t.objectProperty( t.identifier('styles'), makeStyledJsxTag(hash, css, expressions, styleComponentImportName) ), t.objectProperty(t.identifier('className'), className) ]) ) return } const id = path.parentPath.node.id const baseExportName = id ? id.name : 'default' let parentPath = baseExportName === 'default' ? path.parentPath : path.findParent( path => path.isVariableDeclaration() || (path.isAssignmentExpression() && isModuleExports(path.get('left').node)) ) if (baseExportName !== 'default' && !parentPath.parentPath.isProgram()) { parentPath = parentPath.parentPath } const css = cssToBabelType(styles.css) const newPath = t.isArrayExpression(css) ? css : t.newExpression(t.identifier('String'), [css]) // default exports if (baseExportName === 'default') { const defaultExportIdentifier = path.scope.generateUidIdentifier( 'defaultExport' ) parentPath.insertBefore( t.variableDeclaration('const', [ t.variableDeclarator(defaultExportIdentifier, newPath) ]) ) parentPath.insertBefore(addHash(defaultExportIdentifier, styles.hash)) path.replaceWith(defaultExportIdentifier) return } // local and named exports parentPath.insertAfter(addHash(t.identifier(baseExportName), styles.hash)) path.replaceWith(newPath) } function addHash(exportIdentifier, hash) { const value = typeof hash === 'string' ? t.stringLiteral(hash) : hash return t.expressionStatement( t.assignmentExpression( '=', t.memberExpression(exportIdentifier, t.identifier('__hash')), value ) ) } export const visitor = { ImportDeclaration(path, state) { // import css from 'styled-jsx/css' if (path.node.source.value !== 'styled-jsx/css') { return } // Find all the imported specifiers. // e.g import css, { global, resolve } from 'styled-jsx/css' // -> ['css', 'global', 'resolve'] const specifiersNames = path.node.specifiers.map( specifier => specifier.local.name ) specifiersNames.forEach(tagName => { // Get all the reference paths i.e. the places that use the tagName above // eg. // css`div { color: red }` // css.global`div { color: red }` // global`div { color: red ` const binding = path.scope.getBinding(tagName) if (!binding || !Array.isArray(binding.referencePaths)) { return } // Produces an object containing all the TaggedTemplateExpression paths detected. // The object contains { scoped, global, resolve } const taggedTemplateExpressions = binding.referencePaths .map(ref => ref.parentPath) .reduce( (result, path) => { let taggedTemplateExpression if (path.isTaggedTemplateExpression()) { // css`` global`` resolve`` taggedTemplateExpression = path } else if ( path.parentPath && path.isMemberExpression() && path.parentPath.isTaggedTemplateExpression() ) { // This part is for css.global`` or css.resolve`` // using the default import css taggedTemplateExpression = path.parentPath } else { return result } const tag = taggedTemplateExpression.get('tag') const id = tag.isIdentifier() ? tag.node.name : tag.get('property').node.name if (result[id]) { result[id].push(taggedTemplateExpression) } else { result.scoped.push(taggedTemplateExpression) } return result }, { scoped: [], global: [], resolve: [] } ) let hasJSXStyle = false const { vendorPrefixes, sourceMaps } = state.opts Object.keys(taggedTemplateExpressions).forEach(type => taggedTemplateExpressions[type].forEach(path => { hasJSXStyle = true // Process each css block processTaggedTemplateExpression({ type, path, file: state.file, splitRules: typeof state.opts.optimizeForSpeed === 'boolean' ? state.opts.optimizeForSpeed : process.env.NODE_ENV === 'production', plugins: state.plugins, vendorPrefixes, sourceMaps, styleComponentImportName: state.styleComponentImportName }) }) ) const hasCssResolve = hasJSXStyle && taggedTemplateExpressions.resolve.length > 0 // When using the `resolve` helper we need to add an import // for the _JSXStyle component `styled-jsx/style` if (hasCssResolve) { state.file.hasCssResolve = true } }) // Finally remove the import path.remove() } } export default function() { return { Program(path, state) { setStateOptions(state) }, ...visitor } } ================================================ FILE: src/babel-test.js ================================================ import jsx from '@babel/plugin-syntax-jsx' export default function() { return { inherits: jsx, visitor: { JSXOpeningElement(path) { const el = path.node const { name } = el.name || {} if (name !== 'style') { return } el.attributes = el.attributes.filter(a => { const name = a.name.name return name !== 'jsx' && name !== 'global' }) } } } } ================================================ FILE: src/babel.js ================================================ import jsx from '@babel/plugin-syntax-jsx' import { visitor as externalStylesVisitor } from './babel-external' import { isGlobalEl, isStyledJsx, findStyles, makeStyledJsxTag, getJSXStyleInfo, computeClassNames, addClassName, getScope, processCss, createReactComponentImportDeclaration, setStateOptions } from './_utils' import { STYLE_COMPONENT } from './_constants' import { default as babelMacro } from './macro' import { default as babelTest } from './babel-test' export function macro() { return babelMacro(require('babel-plugin-macros')) } export function test() { return babelTest } export default function({ types: t }) { const jsxVisitors = { JSXOpeningElement(path, state) { const el = path.node const { name } = el.name || {} if (!state.hasJSXStyle) { return } if (state.ignoreClosing === null) { // We keep a counter of elements inside so that we // can keep track of when we exit the parent to reset state // note: if we wished to add an option to turn off // selectors to reach parent elements, it would suffice to // set this to `1` and do an early return instead state.ignoreClosing = 0 } const tag = path.get('name') if ( name && name !== 'style' && name !== state.styleComponentImportName && (name.charAt(0) !== name.charAt(0).toUpperCase() || Object.values(path.scope.bindings).some(binding => binding.referencePaths.some(r => r === tag) )) ) { if (state.className) { addClassName(path, state.className) } } state.ignoreClosing++ // Next visit will be: JSXElement exit() }, JSXElement: { enter(path, state) { if (state.hasJSXStyle !== null) { return } const styles = findStyles(path) if (styles.length === 0) { return } state.styles = [] state.externalStyles = [] const scope = getScope(path) for (const style of styles) { // Compute children excluding whitespace const children = style.get('children').filter( c => t.isJSXExpressionContainer(c.node) || // Ignore whitespace around the expression container (t.isJSXText(c.node) && c.node.value.trim() !== '') ) if (children.length !== 1) { throw path.buildCodeFrameError( `Expected one child under ` + `JSX Style tag, but got ${children.length} ` + `(eg: )` ) } const child = children[0] if (!t.isJSXExpressionContainer(child)) { throw path.buildCodeFrameError( `Expected a child of ` + `type JSXExpressionContainer under JSX Style tag ` + `(eg: ), got ${child.type}` ) } const expression = child.get('expression') if (t.isIdentifier(expression)) { const idName = expression.node.name if (expression.scope.hasBinding(idName)) { const externalStylesIdentifier = t.identifier(idName) const isGlobal = isGlobalEl(style.get('openingElement').node) state.externalStyles.push([ t.memberExpression( externalStylesIdentifier, t.identifier('__hash') ), externalStylesIdentifier, isGlobal ]) continue } throw path.buildCodeFrameError( `The Identifier ` + `\`${expression.getSource()}\` is either \`undefined\` or ` + `it is not an external StyleSheet reference i.e. ` + `it doesn't come from an \`import\` or \`require\` statement` ) } if ( !t.isTemplateLiteral(expression) && !t.isStringLiteral(expression) ) { throw path.buildCodeFrameError( `Expected a template ` + `literal or String literal as the child of the ` + `JSX Style tag (eg: ),` + ` but got ${expression.type}` ) } state.styles.push(getJSXStyleInfo(expression, scope)) } let externalJsxId if (state.externalStyles.length > 0) { const expressions = state.externalStyles // Remove globals .filter(s => !s[2]) .map(s => s[0]) const expressionsLength = expressions.length if (expressionsLength === 0) { externalJsxId = null } else { // Construct a template literal of this form: // `jsx-${styles.__scopedHash} jsx-${otherStyles.__scopedHash}` externalJsxId = t.templateLiteral( [ t.templateElement({ raw: 'jsx-', cooked: 'jsx-' }), ...[...new Array(expressionsLength - 1).fill(null)].map(() => t.templateElement({ raw: ' jsx-', cooked: ' jsx-' }) ), t.templateElement({ raw: '', cooked: '' }, true) ], expressions ) } } if (state.styles.length > 0 || externalJsxId) { const { staticClassName, className } = computeClassNames( state.styles, externalJsxId, state.styleComponentImportName ) state.className = className state.staticClassName = staticClassName } state.hasJSXStyle = true state.file.hasJSXStyle = true // Next visit will be: JSXOpeningElement }, exit(path, state) { const isGlobal = isGlobalEl(path.node.openingElement) if (state.hasJSXStyle && !--state.ignoreClosing && !isGlobal) { state.hasJSXStyle = null state.className = null state.externalJsxId = null } if (!state.hasJSXStyle || !isStyledJsx(path)) { return } if (state.ignoreClosing > 1) { let styleTagSrc try { styleTagSrc = path.getSource() } catch (error) {} throw path.buildCodeFrameError( 'Detected nested style tag' + (styleTagSrc ? `: \n\n${styleTagSrc}\n\n` : ' ') + 'styled-jsx only allows style tags ' + 'to be direct descendants (children) of the outermost ' + 'JSX element i.e. the subtree root.' ) } if ( state.externalStyles.length > 0 && path.get('children').filter(child => { if (!t.isJSXExpressionContainer(child)) { return false } const expression = child.get('expression') return expression && expression.isIdentifier() }).length === 1 ) { const [id, css] = state.externalStyles.shift() path.replaceWith( makeStyledJsxTag(id, css, [], state.styleComponentImportName) ) return } const { vendorPrefixes, sourceMaps } = state.opts const stylesInfo = { ...state.styles.shift(), file: state.file, staticClassName: state.staticClassName, isGlobal, plugins: state.plugins, vendorPrefixes, sourceMaps } const splitRules = typeof state.opts.optimizeForSpeed === 'boolean' ? state.opts.optimizeForSpeed : process.env.NODE_ENV === 'production' const { hash, css, expressions } = processCss(stylesInfo, { splitRules }) path.replaceWith( makeStyledJsxTag( hash, css, expressions, state.styleComponentImportName ) ) } } } // only apply JSXFragment visitor if supported if (t.isJSXFragment) { jsxVisitors.JSXFragment = jsxVisitors.JSXElement jsxVisitors.JSXOpeningFragment = { enter(path, state) { if (!state.hasJSXStyle) { return } if (state.ignoreClosing === null) { // We keep a counter of elements inside so that we // can keep track of when we exit the parent to reset state // note: if we wished to add an option to turn off // selectors to reach parent elements, it would suffice to // set this to `1` and do an early return instead state.ignoreClosing = 0 } state.ignoreClosing++ } } } const visitors = { inherits: jsx, visitor: { Program: { enter(path, state) { setStateOptions(state) state.hasJSXStyle = null state.ignoreClosing = null state.file.hasJSXStyle = false state.file.hasCssResolve = false // create unique identifier for _JSXStyle component state.styleComponentImportName = path.scope.generateUidIdentifier( STYLE_COMPONENT ).name // we need to beat the arrow function transform and // possibly others so we traverse from here or else // dynamic values in classNames could be incorrect path.traverse(jsxVisitors, state) // Transpile external styles path.traverse(externalStylesVisitor, state) }, exit(path, state) { if (!state.file.hasJSXStyle && !state.file.hasCssResolve) { return } state.file.hasJSXStyle = true const importDeclaration = createReactComponentImportDeclaration(state) path.unshiftContainer('body', importDeclaration) } } } } return visitors } ================================================ FILE: src/index.js ================================================ import 'client-only' export { StyleRegistry, createStyleRegistry, useStyleRegistry } from './stylesheet-registry' export { default as style } from './style' ================================================ FILE: src/lib/hash.js ================================================ 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] } ================================================ FILE: src/lib/style-transform.js ================================================ import Stylis from 'stylis' import stylisRuleSheet from 'stylis-rule-sheet' const stylis = new Stylis() function disableNestingPlugin(...args) { let [context, , , parent = [], line, column] = args if (context === 2) { // replace null characters and trim // eslint-disable-next-line no-control-regex parent = (parent[0] || '').replace(/\u0000/g, '').trim() if (parent.length > 0 && parent.charAt(0) !== '@') { throw new Error( `Nesting detected at ${line}:${column}. ` + 'Unfortunately nesting is not supported by styled-jsx.' ) } } } let generator let filename let offset function sourceMapsPlugin(...args) { const [context, , , , line, column, length] = args // Pre-processed, init source map if (context === -1 && generator !== undefined) { generator.addMapping({ generated: { line: 1, column: 0 }, source: filename, original: offset }) return } // Post-processed if (context === -2 && generator !== undefined) { generator = undefined offset = undefined filename = undefined return } // Selector/property, update source map if ((context === 1 || context === 2) && generator !== undefined) { generator.addMapping({ generated: { line: 1, column: length }, source: filename, original: { line: line + offset.line, column: column + offset.column } }) } } /** * splitRulesPlugin * Used to split a blob of css into an array of rules * that can inserted via sheet.insertRule */ let splitRules = [] const splitRulesPlugin = stylisRuleSheet(rule => { splitRules.push(rule) }) stylis.use(disableNestingPlugin) stylis.use(sourceMapsPlugin) stylis.use(splitRulesPlugin) stylis.set({ cascade: false, compress: true }) /** * Public transform function * * @param {String} hash * @param {String} styles * @param {Object} settings * @return {string} */ function transform(hash, styles, settings = {}) { generator = settings.generator offset = settings.offset filename = settings.filename splitRules = [] stylis.set({ prefix: typeof settings.vendorPrefixes === 'boolean' ? settings.vendorPrefixes : true }) stylis(hash, styles) if (settings.splitRules) { return splitRules } return splitRules.join('') } export default transform ================================================ FILE: src/lib/stylesheet.js ================================================ /* Based on Glamor's sheet https://github.com/threepointone/glamor/blob/667b480d31b3721a905021b26e1290ce92ca2879/src/sheet.js */ const isProd = typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production' const isString = o => Object.prototype.toString.call(o) === '[object String]' export default class StyleSheet { constructor({ name = 'stylesheet', optimizeForSpeed = isProd } = {}) { invariant(isString(name), '`name` must be a string') this._name = name this._deletedRulePlaceholder = `#${name}-deleted-rule____{}` invariant( typeof optimizeForSpeed === 'boolean', '`optimizeForSpeed` must be a boolean' ) this._optimizeForSpeed = optimizeForSpeed this._serverSheet = undefined this._tags = [] this._injected = false this._rulesCount = 0 const node = typeof window !== 'undefined' && document.querySelector('meta[property="csp-nonce"]') this._nonce = node ? node.getAttribute('content') : null } setOptimizeForSpeed(bool) { invariant( typeof bool === 'boolean', '`setOptimizeForSpeed` accepts a boolean' ) invariant( this._rulesCount === 0, 'optimizeForSpeed cannot be when rules have already been inserted' ) this.flush() this._optimizeForSpeed = bool this.inject() } isOptimizeForSpeed() { return this._optimizeForSpeed } inject() { invariant(!this._injected, 'sheet already injected') this._injected = true if (typeof window !== 'undefined' && this._optimizeForSpeed) { this._tags[0] = this.makeStyleTag(this._name) this._optimizeForSpeed = 'insertRule' in this.getSheet() if (!this._optimizeForSpeed) { if (!isProd) { console.warn( 'StyleSheet: optimizeForSpeed mode not supported falling back to standard mode.' ) } this.flush() this._injected = true } return } this._serverSheet = { cssRules: [], insertRule: (rule, index) => { if (typeof index === 'number') { this._serverSheet.cssRules[index] = { cssText: rule } } else { this._serverSheet.cssRules.push({ cssText: rule }) } return index }, deleteRule: index => { this._serverSheet.cssRules[index] = null } } } getSheetForTag(tag) { if (tag.sheet) { return tag.sheet } // this weirdness brought to you by firefox for (let i = 0; i < document.styleSheets.length; i++) { if (document.styleSheets[i].ownerNode === tag) { return document.styleSheets[i] } } } getSheet() { return this.getSheetForTag(this._tags[this._tags.length - 1]) } insertRule(rule, index) { invariant(isString(rule), '`insertRule` accepts only strings') if (typeof window === 'undefined') { if (typeof index !== 'number') { index = this._serverSheet.cssRules.length } this._serverSheet.insertRule(rule, index) return this._rulesCount++ } if (this._optimizeForSpeed) { const sheet = this.getSheet() if (typeof index !== 'number') { index = sheet.cssRules.length } // this weirdness for perf, and chrome's weird bug // https://stackoverflow.com/questions/20007992/chrome-suddenly-stopped-accepting-insertrule try { sheet.insertRule(rule, index) } catch (error) { if (!isProd) { console.warn( `StyleSheet: illegal rule: \n\n${rule}\n\nSee https://stackoverflow.com/q/20007992 for more info` ) } return -1 } } else { const insertionPoint = this._tags[index] this._tags.push(this.makeStyleTag(this._name, rule, insertionPoint)) } return this._rulesCount++ } replaceRule(index, rule) { if (this._optimizeForSpeed || typeof window === 'undefined') { const sheet = typeof window !== 'undefined' ? this.getSheet() : this._serverSheet if (!rule.trim()) { rule = this._deletedRulePlaceholder } if (!sheet.cssRules[index]) { // @TBD Should we throw an error? return index } sheet.deleteRule(index) try { sheet.insertRule(rule, index) } catch (error) { if (!isProd) { console.warn( `StyleSheet: illegal rule: \n\n${rule}\n\nSee https://stackoverflow.com/q/20007992 for more info` ) } // In order to preserve the indices we insert a deleteRulePlaceholder sheet.insertRule(this._deletedRulePlaceholder, index) } } else { const tag = this._tags[index] invariant(tag, `old rule at index \`${index}\` not found`) tag.textContent = rule } return index } deleteRule(index) { if (typeof window === 'undefined') { this._serverSheet.deleteRule(index) return } if (this._optimizeForSpeed) { this.replaceRule(index, '') } else { const tag = this._tags[index] invariant(tag, `rule at index \`${index}\` not found`) tag.parentNode.removeChild(tag) this._tags[index] = null } } flush() { this._injected = false this._rulesCount = 0 if (typeof window !== 'undefined') { this._tags.forEach(tag => tag && tag.parentNode.removeChild(tag)) this._tags = [] } else { // simpler on server this._serverSheet.cssRules = [] } } cssRules() { if (typeof window === 'undefined') { return this._serverSheet.cssRules } return this._tags.reduce((rules, tag) => { if (tag) { rules = rules.concat( Array.prototype.map.call(this.getSheetForTag(tag).cssRules, rule => rule.cssText === this._deletedRulePlaceholder ? null : rule ) ) } else { rules.push(null) } return rules }, []) } makeStyleTag(name, cssString, relativeToTag) { if (cssString) { invariant( isString(cssString), 'makeStyleTag accepts only strings as second parameter' ) } const tag = document.createElement('style') if (this._nonce) tag.setAttribute('nonce', this._nonce) tag.type = 'text/css' tag.setAttribute(`data-${name}`, '') if (cssString) { tag.appendChild(document.createTextNode(cssString)) } const head = document.head || document.getElementsByTagName('head')[0] if (relativeToTag) { head.insertBefore(tag, relativeToTag) } else { head.appendChild(tag) } return tag } get length() { return this._rulesCount } } function invariant(condition, message) { if (!condition) { throw new Error(`StyleSheet: ${message}.`) } } ================================================ FILE: src/macro.js ================================================ import { processTaggedTemplateExpression } from './babel-external' import { setStateOptions, createReactComponentImportDeclaration } from './_utils' import { STYLE_COMPONENT } from './_constants' export default ({ createMacro, MacroError }) => { return createMacro(styledJsxMacro) function styledJsxMacro({ references, state }) { setStateOptions(state) // Holds a reference to all the lines where strings are tagged using the `css` tag name. // We print a warning at the end of the macro in case there is any reference to css, // because `css` is generally used as default import name for 'styled-jsx/css'. // People who want to migrate from this macro to pure styled-jsx might have name conflicts issues. const cssReferences = [] // references looks like this // { // default: [path, path], // resolve: [path], // } Object.keys(references).forEach(refName => { // Enforce `resolve` as named import so people // can only import { resolve } from 'styled-jsx/macro' // or an alias of it eg. { resolve as foo } if (refName !== 'default' && refName !== 'resolve') { throw new MacroError( `Imported an invalid named import: ${refName}. Please import: resolve` ) } // Start processing the references for refName references[refName].forEach(path => { // We grab the parent path. Eg. // path -> css // path.parenPath -> css`div { color: red }` let templateExpression = path.parentPath // templateExpression member expression? // path -> css // path.parentPath -> css.resolve if (templateExpression.isMemberExpression()) { // grab .resolve const tagPropertyName = templateExpression.get('property').node.name // Member expressions are only valid on default imports // eg. import css from 'styled-jsx/macro' if (refName !== 'default') { throw new MacroError( `Can't use named import ${ path.node.name } as a member expression: ${ path.node.name }.${tagPropertyName}\`div { color: red }\` Please use it directly: ${ path.node.name }\`div { color: red }\`` ) } // Otherwise enforce `css.resolve` if (tagPropertyName !== 'resolve') { throw new MacroError( `Using an invalid tag: ${tagPropertyName}. Please use ${ templateExpression.get('object').node.name }.resolve` ) } // Grab the TaggedTemplateExpression // i.e. css.resolve`div { color: red }` templateExpression = templateExpression.parentPath } else { if (refName === 'default') { const { name } = path.node throw new MacroError( `Can't use default import directly eg. ${name}\`div { color: red }\`. Please use ${name}.resolve\`div { color: red }\` instead.` ) } if (path.node.name === 'css') { // If the path node name is `css` we push it to the references above to emit a warning later. cssReferences.push(path.node.loc.start.line) } } if (!state.styleComponentImportName) { const programPath = path.findParent(p => p.isProgram()) state.styleComponentImportName = programPath.scope.generateUidIdentifier( STYLE_COMPONENT ).name const importDeclaration = createReactComponentImportDeclaration(state) programPath.unshiftContainer('body', importDeclaration) } // Finally transform the path :) processTaggedTemplateExpression({ type: 'resolve', path: templateExpression, file: state.file, splitRules: typeof state.opts.optimizeForSpeed === 'boolean' ? state.opts.optimizeForSpeed : process.env.NODE_ENV === 'production', plugins: state.plugins, vendorPrefixes: state.opts.vendorPrefixes, sourceMaps: state.opts.sourceMaps, styleComponentImportName: state.styleComponentImportName }) }) }) if (cssReferences.length > 0) { console.warn( `styled-jsx - Warning - We detected that you named your tag as \`css\` at lines: ${cssReferences.join( ', ' )}.\n` + 'This tag name is usually used as default import name for `styled-jsx/css`.\n' + 'Porting macro code to pure styled-jsx in the future might be a bit problematic.' ) } } } ================================================ FILE: src/style.js ================================================ import React, { useLayoutEffect, useRef } from 'react' import { useStyleRegistry, createStyleRegistry } from './stylesheet-registry' import { computeId } from './lib/hash' // Opt-into the new `useInsertionEffect` API in React 18, fallback to `useLayoutEffect`. // https://github.com/reactwg/react-18/discussions/110 const useInsertionEffect = React.useInsertionEffect || useLayoutEffect const defaultRegistry = typeof window !== 'undefined' ? createStyleRegistry() : undefined export default function JSXStyle(props) { const registry = defaultRegistry ? defaultRegistry : useStyleRegistry() const insertionEffectCalled = useRef(false) // `registry` might not exist while server-side rendering if (!registry) { return null } if (typeof window === 'undefined') { registry.add(props) return null } useInsertionEffect(() => { // ReactDOM removes all DOM during hydration in certain cases if (!document.head) { return } registry.add(props) insertionEffectCalled.current = true return () => { insertionEffectCalled.current = false registry.remove(props) } }, [props.id, String(props.dynamic)]) useLayoutEffect(() => { if (!document.head || insertionEffectCalled.current) { return } registry.add(props) return () => { registry.remove(props) } // props.children can be string[], will be striped since id is identical }, [props.id, String(props.dynamic)]) return null } JSXStyle.dynamic = info => { return info .map(tagInfo => { const baseId = tagInfo[0] const props = tagInfo[1] return computeId(baseId, props) }) .join(' ') } ================================================ FILE: src/stylesheet-registry.js ================================================ import React, { useState, useContext, createContext } from 'react' 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 } = {}) { this._sheet = styleSheet || new DefaultStyleSheet({ name: 'styled-jsx', optimizeForSpeed }) this._sheet.inject() if (styleSheet && typeof optimizeForSpeed === 'boolean') { this._sheet.setOptimizeForSpeed(optimizeForSpeed) this._optimizeForSpeed = this._sheet.isOptimizeForSpeed() } this._fromServer = undefined this._indices = {} this._instancesCounts = {} } add(props) { if (undefined === this._optimizeForSpeed) { this._optimizeForSpeed = Array.isArray(props.children) this._sheet.setOptimizeForSpeed(this._optimizeForSpeed) this._optimizeForSpeed = this._sheet.isOptimizeForSpeed() } if (typeof window !== 'undefined' && !this._fromServer) { this._fromServer = this.selectFromServer() this._instancesCounts = Object.keys(this._fromServer).reduce( (acc, tagName) => { acc[tagName] = 0 return acc }, {} ) } const { styleId, rules } = this.getIdAndRules(props) // Deduping: just increase the instances count. if (styleId in this._instancesCounts) { this._instancesCounts[styleId] += 1 return } const indices = rules .map(rule => this._sheet.insertRule(rule)) // Filter out invalid rules .filter(index => index !== -1) this._indices[styleId] = indices this._instancesCounts[styleId] = 1 } remove(props) { const { styleId } = this.getIdAndRules(props) invariant( styleId in this._instancesCounts, `styleId: \`${styleId}\` not found` ) this._instancesCounts[styleId] -= 1 if (this._instancesCounts[styleId] < 1) { const tagFromServer = this._fromServer && this._fromServer[styleId] if (tagFromServer) { tagFromServer.parentNode.removeChild(tagFromServer) delete this._fromServer[styleId] } else { this._indices[styleId].forEach(index => this._sheet.deleteRule(index)) delete this._indices[styleId] } delete this._instancesCounts[styleId] } } update(props, nextProps) { this.add(nextProps) this.remove(props) } flush() { this._sheet.flush() this._sheet.inject() this._fromServer = undefined this._indices = {} this._instancesCounts = {} } cssRules() { const fromServer = this._fromServer ? Object.keys(this._fromServer).map(styleId => [ styleId, this._fromServer[styleId] ]) : [] const cssRules = this._sheet.cssRules() return fromServer.concat( Object.keys(this._indices) .map(styleId => [ styleId, this._indices[styleId] .map(index => cssRules[index].cssText) .join(this._optimizeForSpeed ? '' : '\n') ]) // filter out empty rules .filter(rule => Boolean(rule[1])) ) } styles(options) { return mapRulesToStyle(this.cssRules(), options) } getIdAndRules(props) { const { children: css, dynamic, id } = props if (dynamic) { const styleId = computeId(id, dynamic) return { styleId, rules: Array.isArray(css) ? css.map(rule => computeSelector(styleId, rule)) : [computeSelector(styleId, css)] } } return { styleId: computeId(id), rules: Array.isArray(css) ? css : [css] } } /** * selectFromServer * * Collects style tags from the document with id __jsx-XXX */ selectFromServer() { const elements = Array.prototype.slice.call( document.querySelectorAll('[id^="__jsx-"]') ) return elements.reduce((acc, element) => { const id = element.id.slice(2) acc[id] = element return acc }, {}) } } function invariant(condition, message) { if (!condition) { throw new Error(`StyleSheetRegistry: ${message}.`) } } export const StyleSheetContext = createContext(null) StyleSheetContext.displayName = 'StyleSheetContext' 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) } ================================================ FILE: src/webpack.js ================================================ import loaderUtils from 'loader-utils' const types = ['scoped', 'global', 'resolve'] export default function(content) { if (this.cacheable) this.cacheable() this.addDependency(this.resourcePath) const options = Object.assign({}, loaderUtils.getOptions(this)) if (!options.type) { options.type = 'scoped' } // Calls type with the current file name. if (typeof options.type === 'function') { options.type = options.type(this.resourcePath, { query: loaderUtils.parseQuery(this.resourceQuery || '?') || {} }) } if (!types.includes(options.type)) { return this.callback( 'The given `type` option is invalid. \n\n' + `Expected:\n One of scoped|global|resolve \n\n` + 'Actual:\n ' + options.type ) } // Allows to define the type for each individual file using a CSS comment. const commentType = content.match(/\/*\s*@styled-jsx=(scoped|global|resolve)/) if (commentType) { options.type = commentType[1] } let output = `import css from 'styled-jsx/css';\n\nconst styles = css` if (options.type === 'global') { // css.global`` output += '.global' } else if (options.type === 'resolve') { // css.resolve`` output += '.resolve' } // default css`` // Escape backticks and backslashes: “`” ⇒ “\`”, “\” ⇒ “\\” // (c) https://github.com/coox/styled-jsx-css-loader/blob/97a38e90dddf2c4b066e9247db0612c8f95302de/index.js#L6 output += `\`${content.replace( /[`\\]/g, match => '\\' + match )}\`;\n\nexport default styles;` this.callback(null, output) } ================================================ FILE: style.d.ts ================================================ declare module 'styled-jsx/style' { export default function JSXStyle(props: any): null } ================================================ FILE: style.js ================================================ module.exports = require('./dist/index').style ================================================ FILE: test/.babelrc ================================================ { "presets": ["@babel/preset-env", "@babel/preset-react"], "plugins": [ "@babel/plugin-proposal-object-rest-spread", "@babel/plugin-transform-runtime" ], "sourceMaps": false } ================================================ FILE: test/__snapshots__/attribute.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`generate attribute for mixed modes (global, static, dynamic) 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import styles from './styles'; const styles2 = require('./styles2'); // external only export const Test1 = () =>

external only

<_JSXStyle id={styles.__hash}>{styles} <_JSXStyle id={styles2.__hash}>{styles2}
; // external and static export const Test2 = () =>

external and static

<_JSXStyle id={\\"2982525546\\"}>{\\"p.jsx-2982525546{color:red;}\\"} <_JSXStyle id={styles.__hash}>{styles}
; // external and dynamic export const Test3 = ({ color }) =>

external and dynamic

<_JSXStyle id={\\"1947484460\\"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:\${color};}\`} <_JSXStyle id={styles.__hash}>{styles}
; // external, static and dynamic export const Test4 = ({ color }) =>

external, static and dynamic

<_JSXStyle id={\\"3190985107\\"}>{\\"p.jsx-3190985107{display:inline-block;}\\"} <_JSXStyle id={\\"1336444426\\"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:\${color};}\`} <_JSXStyle id={styles.__hash}>{styles}
; // static only export const Test5 = () =>

static only

<_JSXStyle id={\\"3190985107\\"}>{\\"p.jsx-1372669040{display:inline-block;}\\"} <_JSXStyle id={\\"2982525546\\"}>{\\"p.jsx-1372669040{color:red;}\\"}
; // static and dynamic export const Test6 = ({ color }) =>

static and dynamic

<_JSXStyle id={\\"3190985107\\"}>{\\"p.jsx-3190985107{display:inline-block;}\\"} <_JSXStyle id={\\"1336444426\\"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:\${color};}\`}
; // dynamic only export const Test7 = ({ color }) =>

dynamic only

<_JSXStyle id={\\"1947484460\\"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:\${color};}\`}
; // dynamic with scoped compound variable export const Test8 = ({ color }) => { if (color) { const innerProps = { color }; return

dynamic with scoped compound variable

<_JSXStyle id={\\"1791723528\\"} dynamic={[innerProps.color]}>{\`p.__jsx-style-dynamic-selector{color:\${innerProps.color};}\`}
; } }; // dynamic with compound variable export const Test9 = ({ color }) => { const innerProps = { color }; return

dynamic with compound variable

<_JSXStyle id={\\"248922593\\"} dynamic={[innerProps.color]}>{\`p.__jsx-style-dynamic-selector{color:\${innerProps.color};}\`}
; }; const foo = 'red'; // dynamic with constant variable export const Test10 = () =>

dynamic with constant variable

<_JSXStyle id={\\"461505126\\"}>{\`p.jsx-461505126{color:\${foo};}\`}
; // dynamic with complex scope export const Test11 = ({ color }) => { const items = Array.from({ length: 5 }).map((item, i) =>
  • <_JSXStyle id={\\"2172653867\\"} dynamic={[color]}>{\`.item.__jsx-style-dynamic-selector{color:\${color};}\`} Item #{i + 1}
  • ); return
      {items}
    ; };" `; exports[`rewrites className 1`] = ` "var _this = this; import _JSXStyle from \\"styled-jsx/style\\"; export default (() => { const Element = 'div'; return
    <_JSXStyle id={\\"2886504620\\"}>{\\"div.jsx-2886504620{color:red;}\\"}
    ; });" `; ================================================ FILE: test/__snapshots__/external.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`(optimized) transpiles external stylesheets (CommonJS modules) 1`] = ` "const _defaultExport = ['div.jsx-2292456818{font-size:3em;}']; _defaultExport.__hash = '2292456818'; module.exports = _defaultExport;" `; exports[`(optimized) transpiles external stylesheets 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import colors, { size } from './constants'; const color = 'red'; const bar = ['div.jsx-2141779268{font-size:3em;}']; bar.__hash = '2141779268'; const baz = ['div{font-size:3em;}']; baz.__hash = '2141779268'; a.__hash = '262929833'; const a = [\`div{font-size:\${size}em;}\`]; export const uh = bar; export const foo = [\`div.jsx-2299908427{color:\${color};}\`]; foo.__hash = '2299908427'; ({ styles: <_JSXStyle id={\\"1329679275\\"}>{[\`div.jsx-1329679275{color:\${colors.green.light};}\`, 'a.jsx-1329679275{color:red;}']}, className: 'jsx-1329679275' }); const b = { styles: <_JSXStyle id={\\"1329679275\\"}>{[\`div.jsx-1329679275{color:\${colors.green.light};}\`, 'a.jsx-1329679275{color:red;}']}, className: 'jsx-1329679275' }; const dynamic = colors => { const b = { styles: <_JSXStyle id={\\"3325296745\\"} dynamic={[colors.green.light]}>{[\`div.__jsx-style-dynamic-selector{color:\${colors.green.light};}\`, 'a.__jsx-style-dynamic-selector{color:red;}']}, className: _JSXStyle.dynamic([['3325296745', [colors.green.light]]]) }; }; export default { styles: <_JSXStyle id={\\"3290112549\\"}>{['div.jsx-3290112549{font-size:3em;}', \`p.jsx-3290112549{color:\${color};}\`]}, className: 'jsx-3290112549' };" `; exports[`Makes sure that style nodes are not re-used 1`] = ` "\\"use strict\\"; var _style = _interopRequireDefault(require(\\"styled-jsx/style\\")); var _App = _interopRequireDefault(require(\\"./App.styles\\")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function Test() { return
    <_style.default id={_App.default.__hash}>{_App.default}
    ; }" `; exports[`does not transpile non-styled-jsx tagged teplate literals 1`] = ` "import css from 'hell'; const color = 'red'; const bar = css\` div { font-size: 3em; } \`; export const uh = bar; export const foo = css\`div { color: \${color}}\`; export default css\` div { font-size: 3em; } p { color: \${color}; } \`; const Title = styled.h1\` color: red; font-size: 50px; \`; const AnotherTitle = Title.extend\`color: blue;\`; export const Component = () => My page;" `; exports[`injects JSXStyle for nested scope 1`] = ` "import _JSXStyle from 'styled-jsx/style'; function test() { ({ styles: <_JSXStyle id={\\"2886504620\\"}>{\\"div.jsx-2886504620{color:red;}\\"}, className: 'jsx-2886504620' }); }" `; exports[`transpiles external stylesheets (CommonJS modules) 1`] = ` "const _defaultExport = new String('div.jsx-2292456818{font-size:3em;}'); _defaultExport.__hash = '2292456818'; module.exports = _defaultExport;" `; exports[`transpiles external stylesheets 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import colors, { size } from './constants'; const color = 'red'; const bar = new String('div.jsx-2141779268{font-size:3em;}'); bar.__hash = '2141779268'; const baz = new String('div{font-size:3em;}'); baz.__hash = '2141779268'; a.__hash = '262929833'; const a = new String(\`div{font-size:\${size}em;}\`); export const uh = bar; export const foo = new String(\`div.jsx-2299908427{color:\${color};}\`); foo.__hash = '2299908427'; ({ styles: <_JSXStyle id={\\"1329679275\\"}>{\`div.jsx-1329679275{color:\${colors.green.light};}a.jsx-1329679275{color:red;}\`}, className: 'jsx-1329679275' }); const b = { styles: <_JSXStyle id={\\"1329679275\\"}>{\`div.jsx-1329679275{color:\${colors.green.light};}a.jsx-1329679275{color:red;}\`}, className: 'jsx-1329679275' }; const dynamic = colors => { const b = { styles: <_JSXStyle id={\\"3325296745\\"} dynamic={[colors.green.light]}>{\`div.__jsx-style-dynamic-selector{color:\${colors.green.light};}a.__jsx-style-dynamic-selector{color:red;}\`}, className: _JSXStyle.dynamic([['3325296745', [colors.green.light]]]) }; }; export default { styles: <_JSXStyle id={\\"3290112549\\"}>{\`div.jsx-3290112549{font-size:3em;}p.jsx-3290112549{color:\${color};}\`}, className: 'jsx-3290112549' };" `; exports[`use external stylesheet and dynamic element 1`] = ` "import _JSXStyle from \\"styled-jsx/style\\"; import styles from './styles2'; export default (({ level = 1 }) => { const Element = \`h\${level}\`; return

    dynamic element

    <_JSXStyle id={styles.__hash}>{styles}
    ; });" `; exports[`use external stylesheets (global only) 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import styles, { foo as styles3 } from './styles'; const styles2 = require('./styles2'); export default (() =>

    test

    woot

    woot

    <_JSXStyle id={styles2.__hash}>{styles2} <_JSXStyle id={styles3.__hash}>{styles3} <_JSXStyle id={styles.__hash}>{styles}
    );" `; exports[`use external stylesheets (multi-line) 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import styles from './styles'; export default (() =>

    test

    <_JSXStyle id={styles.__hash}>{styles}
    );" `; exports[`use external stylesheets 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import styles from './styles'; const styles2 = require('./styles2'); import { foo as styles3 } from './styles'; export default (() =>

    test

    woot

    <_JSXStyle id={styles2.__hash}>{styles2} <_JSXStyle id={styles3.__hash}>{styles3}
    woot
    <_JSXStyle id={\\"1646697228\\"}>{\\"p.jsx-1646697228{color:red;}div.jsx-1646697228{color:green;}\\"} <_JSXStyle id={styles.__hash}>{styles}
    ); export const Test = () =>

    test

    woot

    <_JSXStyle id={styles3.__hash}>{styles3}
    woot
    <_JSXStyle id={\\"1646697228\\"}>{\\"p.jsx-1646697228{color:red;}div.jsx-1646697228{color:green;}\\"}
    ;" `; ================================================ FILE: test/__snapshots__/index.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`generates source maps 1`] = ` "import _JSXStyle from 'styled-jsx/style'; export default (() =>

    test

    woot

    <_JSXStyle id={\\"2743241663\\"}>{\\"p.jsx-2743241663{color:red;}\\\\n/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNvdXJjZS1tYXBzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUlnQixBQUNjLFVBQUMiLCJmaWxlIjoic291cmNlLW1hcHMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCAoKSA9PiAoXG4gIDxkaXY+XG4gICAgPHA+dGVzdDwvcD5cbiAgICA8cD53b290PC9wPlxuICAgIDxzdHlsZSBqc3g+eydwIHsgY29sb3I6IHJlZCB9J308L3N0eWxlPlxuICA8L2Rpdj5cbilcbiJdfQ== */\\\\n/*@ sourceURL=source-maps.js */\\"}
    );" `; exports[`ignores when attribute is absent 1`] = ` "const a = () =>

    hi

    ;" `; exports[`ignores whitespace around expression container 1`] = ` "import _JSXStyle from 'styled-jsx/style'; export default (() =>

    test

    woot

    woot

    <_JSXStyle id={\\"2743241663\\"}>{\\"p.jsx-2743241663{color:red;}\\"}
    );" `; exports[`mixed global and scoped 1`] = ` "import _JSXStyle from 'styled-jsx/style'; const Test = () => <_JSXStyle id={\\"2743241663\\"}>{\\"p{color:red;}\\"}; export default (() =>

    test

    <_JSXStyle id={\\"4269072806\\"}>{\\"body{background:red;}\\"} <_JSXStyle id={\\"2743241663\\"}>{\\"p.jsx-2673076688{color:red;}\\"}
    );" `; exports[`should have different jsx ids 1`] = ` "import _JSXStyle from 'styled-jsx/style'; const color = 'red'; const otherColor = 'green'; const A = () =>

    test

    <_JSXStyle id={\\"57381496\\"}>{\`p.jsx-57381496{color:\${color};}\`}
    ; const B = () =>

    test

    <_JSXStyle id={\\"3099245642\\"}>{\`p.jsx-3099245642{color:\${otherColor};}\`}
    ; export default (() => );" `; exports[`should not add the data-jsx attribute to components instances 1`] = ` "import _JSXStyle from \\"styled-jsx/style\\"; const Test = () =>
    test <_JSXStyle id={\\"2529315885\\"}>{\\"span.jsx-2529315885{color:red;}\\"}
    ;" `; exports[`works with class 1`] = ` "import _JSXStyle from \\"styled-jsx/style\\"; export default class { render() { return

    test

    <_JSXStyle id={\\"2101845350\\"}>{\\"p.jsx-2101845350{color:red;}\\"}
    ; } }" `; exports[`works with css tagged template literals in the same file 1`] = ` "import _JSXStyle from 'styled-jsx/style'; export default (({ children }) =>

    {children}

    <_JSXStyle id={styles.__hash}>{styles}
    ); const styles = new String('p.jsx-2587355013{color:red;}'); styles.__hash = '2587355013'; class Test extends React.Component { render() { return

    {this.props.children}

    <_JSXStyle id={styles.__hash}>{styles}
    ; } }" `; exports[`works with dynamic element 1`] = ` "import _JSXStyle from \\"styled-jsx/style\\"; export default (({ level = 1 }) => { const Element = \`h\${level}\`; return

    dynamic element

    <_JSXStyle id={\\"1253978709\\"}>{\\".root.jsx-1253978709{background:red;}\\"}
    ; }); export const TestLowerCase = ({ level = 1 }) => { const element = \`h\${level}\`; return

    dynamic element

    <_JSXStyle id={\\"1253978709\\"}>{\\".root.jsx-1253978709{background:red;}\\"}
    ; }; const Element2 = 'div'; export const Test2 = () => { return

    dynamic element

    <_JSXStyle id={\\"1253978709\\"}>{\\".root.jsx-1253978709{background:red;}\\"}
    ; };" `; exports[`works with dynamic element in class 1`] = ` "import _JSXStyle from 'styled-jsx/style'; export default class { render() { const Element = 'div'; return

    dynamic element

    <_JSXStyle id={\\"1800172487\\"}>{\\".root.jsx-1800172487{background:red;}\\"}
    ; } } const Element2 = 'div'; export const Test2 = class { render() { return

    dynamic element

    <_JSXStyle id={\\"1800172487\\"}>{\\".root.jsx-1800172487{background:red;}\\"}
    ; } };" `; exports[`works with expressions in template literals 1`] = ` "import _JSXStyle from 'styled-jsx/style'; const darken = c => c; const color = 'red'; const otherColor = 'green'; const mediumScreen = '680px'; const animationDuration = '200ms'; const animationName = 'my-cool-animation'; const obj = { display: 'block' }; export default (({ display }) =>

    test

    <_JSXStyle id={\\"1003380713\\"}>{\`p.\${color}.jsx-3922596756{color:\${otherColor};display:\${obj.display};}\`} <_JSXStyle id={\\"2743241663\\"}>{\\"p.jsx-3922596756{color:red;}\\"} <_JSXStyle id={\\"602592955\\"}>{\`body{background:\${color};}\`} <_JSXStyle id={\\"602592955\\"}>{\`body{background:\${color};}\`} <_JSXStyle id={\\"128557999\\"}>{\`p.jsx-3922596756{color:\${color};}\`} <_JSXStyle id={\\"128557999\\"}>{\`p.jsx-3922596756{color:\${color};}\`} <_JSXStyle id={\\"2622100973\\"}>{\`p.jsx-3922596756{color:\${darken(color)};}\`} <_JSXStyle id={\\"1167419394\\"}>{\`p.jsx-3922596756{color:\${darken(color) + 2};}\`} <_JSXStyle id={\\"4052509549\\"}>{\`@media (min-width:\${mediumScreen}){p.jsx-3922596756{color:green;}p.jsx-3922596756{color:\${\`red\`};}}p.jsx-3922596756{color:red;}\`} <_JSXStyle id={\\"2824547816\\"}>{\`p.jsx-3922596756{-webkit-animation-duration:\${animationDuration};animation-duration:\${animationDuration};}\`} <_JSXStyle id={\\"417951176\\"}>{\`p.jsx-3922596756{-webkit-animation:\${animationDuration} forwards \${animationName};animation:\${animationDuration} forwards \${animationName};}div.jsx-3922596756{background:\${color};}\`} <_JSXStyle id={\\"1795062918\\"} dynamic={[display ? 'block' : 'none']}>{\`span.__jsx-style-dynamic-selector{display:\${display ? 'block' : 'none'};}\`}
    );" `; exports[`works with global styles 1`] = ` "import _JSXStyle from 'styled-jsx/style'; const Test = () =>
    <_JSXStyle id={\\"2209073070\\"}>{\\"body{color:red;}:hover{color:red;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-animation:foo 1s ease-out;animation:foo 1s ease-out;}div a{display:none;}[data-test]>div{color:red;}\\"}
    ; const Test2 = () => <_JSXStyle id={\\"2743241663\\"}>{\\"p{color:red;}\\"};" `; exports[`works with multiple jsx blocks 1`] = ` "import _JSXStyle from \\"styled-jsx/style\\"; const attrs = { id: 'test' }; const Test1 = () =>
    test <_JSXStyle id={\\"2529315885\\"}>{\\"span.jsx-2529315885{color:red;}\\"}
    ; const Test2 = () => test; const Test3 = () =>
    test <_JSXStyle id={\\"2529315885\\"}>{\\"span.jsx-2529315885{color:red;}\\"}
    ; export default class { render() { return

    test

    <_JSXStyle id={\\"2101845350\\"}>{\\"p.jsx-2101845350{color:red;}\\"}
    ; } }" `; exports[`works with non styled-jsx styles 1`] = ` "import _JSXStyle from 'styled-jsx/style'; export default (() =>

    woot

    <_JSXStyle id={\\"2743241663\\"}>{\\"p.jsx-2743241663{color:red;}\\"}
    );" `; exports[`works with stateless 1`] = ` "import _JSXStyle from 'styled-jsx/style'; export default (() =>

    test

    woot

    woot

    <_JSXStyle id={\\"2743241663\\"}>{\\"p.jsx-2743241663{color:red;}\\"}
    );" `; ================================================ FILE: test/__snapshots__/macro.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`can alias the named import 1`] = ` "import _JSXStyle from 'styled-jsx/style'; ({ styles: <_JSXStyle id={\\"2886504620\\"}>{\\"div.jsx-2886504620{color:red;}\\"}, className: 'jsx-2886504620' });" `; exports[`injects JSXStyle for nested scope 1`] = ` "import _JSXStyle from 'styled-jsx/style'; function test() { ({ styles: <_JSXStyle id={\\"2886504620\\"}>{\\"div.jsx-2886504620{color:red;}\\"}, className: 'jsx-2886504620' }); }" `; exports[`transpiles correctly 1`] = ` "import _JSXStyle from 'styled-jsx/style'; const { className, styles } = { styles: <_JSXStyle id={\\"2052294191\\"}>{\\"div.jsx-2052294191{color:red;}\\"}, className: 'jsx-2052294191' }; const dynamicStyles = props => ({ styles: <_JSXStyle id={\\"290194820\\"} dynamic={[props.color]}>{\`div.__jsx-style-dynamic-selector{color:\${props.color};}\`}, className: _JSXStyle.dynamic([['290194820', [props.color]]]) }); const test = { styles: <_JSXStyle id={\\"2052294191\\"}>{\\"div.jsx-2052294191{color:red;}\\"}, className: 'jsx-2052294191' }; const dynamicStyles2 = props => ({ styles: <_JSXStyle id={\\"290194820\\"} dynamic={[props.color]}>{\`div.__jsx-style-dynamic-selector{color:\${props.color};}\`}, className: _JSXStyle.dynamic([['290194820', [props.color]]]) }); const ExampleComponent = props => { const { className, styles } = dynamicStyles(props); return
    howdy {styles}
    ; };" `; ================================================ FILE: test/__snapshots__/plugins.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`applies plugins 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import styles from './styles'; const color = 'red'; export default (() =>

    test

    <_JSXStyle id={\\"3382438999\\"}>{\`span.\${color}.jsx-3382438999{color:\${otherColor};}\`} <_JSXStyle id={styles.__hash}>{styles}
    );" `; exports[`babel-test plugin strips jsx attribute 1`] = ` "import styles from './styles'; const color = 'red'; export default (() =>

    test

    );" `; exports[`passes options to plugins 1`] = ` "import _JSXStyle from 'styled-jsx/style'; import styles from './styles'; const color = 'red'; export default (() =>

    test

    <_JSXStyle id={\\"3382438999\\"}>{\\".test.jsx-3382438999{content:\\\\\\"{\\\\\\"foo\\\\\\":false,\\\\\\"babel\\\\\\":{\\\\\\"location\\\\\\":{\\\\\\"start\\\\\\":{\\\\\\"line\\\\\\":7,\\\\\\"column\\\\\\":16},\\\\\\"end\\\\\\":{\\\\\\"line\\\\\\":11,\\\\\\"column\\\\\\":5}},\\\\\\"vendorPrefixes\\\\\\":false,\\\\\\"sourceMaps\\\\\\":false,\\\\\\"isGlobal\\\\\\":false}}\\\\\\";}\\"} <_JSXStyle id={styles.__hash}>{styles}
    );" `; ================================================ FILE: test/__snapshots__/styles.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`transpile styles with attributes 1`] = `"html.jsx-123{background-image: linear-gradient(0deg,rgba(255,255,255,0.8),rgba(255,255,255,0.8)), url(/static/background.svg);}p{color:blue;}p{color:blue;}p,a.jsx-123{color:blue;}.foo + a{color:red;}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;}p.jsx-123{color:red;}p.jsx-123{color:red;}*.jsx-123{color:blue;}[href=\\"woot\\"].jsx-123{color:red;}p.jsx-123 a.jsx-123 span.jsx-123{color:red;}p.jsx-123 span{background:blue;}p.jsx-123 a[title=\\"'w ' ' t'\\"].jsx-123{margin:auto;}p.jsx-123 span:not(.test){color:green;}p.jsx-123,h1.jsx-123{color:blue;-webkit-animation:hahaha-jsx-123 3s ease forwards infinite;animation:hahaha-jsx-123 3s ease forwards infinite;-webkit-animation-name:hahaha-jsx-123;animation-name:hahaha-jsx-123;-webkit-animation-delay:100ms;animation-delay:100ms;}p.jsx-123{-webkit-animation:hahaha-jsx-123 1s,hehehe-jsx-123 2s;animation:hahaha-jsx-123 1s,hehehe-jsx-123 2s;}p.jsx-123:hover{color:red;}p.jsx-123::before{color:red;}.jsx-123:hover{color:red;}.jsx-123::before{color:red;}.jsx-123:hover p.jsx-123{color:red;}p.jsx-123+a.jsx-123{color:red;}p.jsx-123~a.jsx-123{color:red;}p.jsx-123>a.jsx-123{color:red;}p.jsx-123>>a.jsx-123{color:red;}@-webkit-keyframes hahaha-jsx-123{from{top:0;}to{top:100;}}@keyframes hahaha-jsx-123{from{top:0;}to{top:100;}}@-webkit-keyframes hehehe-jsx-123{from{left:0;}to{left:100;}}@keyframes hehehe-jsx-123{from{left:0;}to{left:100;}}@media (min-width:500px){.test.jsx-123{color:red;}}.test.jsx-123{display:block;}.inline-flex.jsx-123{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;}.flex.jsx-123{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.test.jsx-123{box-shadow:0 0 10px black,inset 0 0 5px black;}.test[title=\\",\\"].jsx-123{display:inline-block;}.test.is-status.jsx-123 .test.jsx-123{color:red;}.a-selector.jsx-123:hover,.a-selector.jsx-123:focus{outline:none;}@media (min-width:1px) and (max-width:768px){[class*='grid__col--'].jsx-123{margin-top:12px;margin-bottom:12px;}}@media (max-width:64em){.test.jsx-123{margin-bottom:1em;}@supports (-moz-appearance:none) and (display:contents){.test.jsx-123{margin-bottom:2rem;}}}"`; ================================================ FILE: test/_read.js ================================================ import { resolve } from 'path' import { promises as fs } from 'fs' export default async path => { const buffer = await fs.readFile(resolve(__dirname, path)) return buffer.toString() } ================================================ FILE: test/_transform.js ================================================ import path from 'path' import { transform, transformFile } from '@babel/core' export default (file, opts = {}) => new Promise((resolve, reject) => { transformFile( path.resolve(__dirname, file), { babelrc: false, ...opts }, (error, data) => { if (error) { return reject(error) } resolve(data) } ) }) export const transformSource = (src, opts = {}) => new Promise((resolve, reject) => { transform( src, { babelrc: false, ...opts }, (error, result) => { if (error) { return reject(error) } resolve(result) } ) }) ================================================ FILE: test/attribute.js ================================================ // Packages import test from 'ava' // Ours import plugin from '../src/babel' import _transform from './_transform' const transform = (file, opts = {}) => _transform(file, { plugins: [plugin], ...opts }) test('rewrites className', async t => { const { code } = await transform( './fixtures/attribute-generation-classname-rewriting.js' ) t.snapshot(code) }) test('generate attribute for mixed modes (global, static, dynamic)', async t => { const { code } = await transform('./fixtures/attribute-generation-modes.js') t.snapshot(code) }) ================================================ FILE: test/external.js ================================================ // Packages import test from 'ava' // Ours import plugin from '../src/babel' import _transform, { transformSource as _transformSource } from './_transform' const transform = (file, opts = {}) => _transform(file, { plugins: [[plugin, opts]] }) const transformSource = (src, opts = {}) => _transformSource(src.trim(), { plugins: [[plugin, opts]], ...opts }) test('transpiles external stylesheets', async t => { const { code } = await transform('./fixtures/styles.js') t.snapshot(code) }) test('(optimized) transpiles external stylesheets', async t => { const { code } = await transform('./fixtures/styles.js', { optimizeForSpeed: true }) t.snapshot(code) }) test('transpiles external stylesheets (CommonJS modules)', async t => { const { code } = await transform('./fixtures/styles2.js') t.snapshot(code) }) test('(optimized) transpiles external stylesheets (CommonJS modules)', async t => { const { code } = await transform('./fixtures/styles2.js', { optimizeForSpeed: true }) t.snapshot(code) }) test('does not transpile non-styled-jsx tagged teplate literals', async t => { const { code } = await transform( './fixtures/not-styled-jsx-tagged-templates.js' ) t.snapshot(code) }) test('throws when using `this.something` in external stylesheets', async t => { const { message } = await t.throwsAsync(() => transform('./fixtures/styles-external-invalid.js') ) t.regex(message, /this\.props/) }) test('throws when referring an undefined value in external stylesheets', async t => { const { message } = await t.throwsAsync(() => transform('./fixtures/styles-external-invalid2.js') ) t.regex(message, /props\.color/) }) test('use external stylesheets', async t => { const { code } = await transform('./fixtures/external-stylesheet.js') t.snapshot(code) }) test('use external stylesheets (multi-line)', async t => { const { code } = await transform( './fixtures/external-stylesheet-multi-line.js' ) t.snapshot(code) }) test('use external stylesheets (global only)', async t => { const { code } = await transform('./fixtures/external-stylesheet-global.js') t.snapshot(code) }) test('injects JSXStyle for nested scope', async t => { const { code } = await transformSource(` import css from 'styled-jsx/css' function test() { css.resolve\`div { color: red }\` } `) t.snapshot(code) }) test('use external stylesheet and dynamic element', async t => { const { code } = await transform('./fixtures/dynamic-element-external.js') t.snapshot(code) }) test('Makes sure that style nodes are not re-used', async t => { const { code } = await transformSource( ` import styles from './App.styles'; function Test() { return
    } `, { babelrc: false, plugins: [plugin, '@babel/plugin-transform-modules-commonjs'] } ) t.snapshot(code) }) test('Make sure that it works with the new automatic transform', async t => { const { code } = await transformSource( ` import css from "styled-jsx/css"; const A = css.resolve\` div { color: green; } \`; export default function IndexPage() { return JSON.stringify(A); } `, { babelrc: false, presets: [['@babel/preset-react', { runtime: 'automatic' }]], plugins: [plugin] } ) t.snapshot(code) }) ================================================ FILE: test/fixtures/absent.js ================================================ const a = () => (

    hi

    ) ================================================ FILE: test/fixtures/attribute-generation-classname-rewriting.js ================================================ export default () => { const Element = 'div' return (
    ) } ================================================ FILE: test/fixtures/attribute-generation-modes.js ================================================ import styles from './styles' const styles2 = require('./styles2') // external only export const Test1 = () => (

    external only

    ) // external and static export const Test2 = () => (

    external and static

    ) // external and dynamic export const Test3 = ({ color }) => (

    external and dynamic

    ) // external, static and dynamic export const Test4 = ({ color }) => (

    external, static and dynamic

    ) // static only export const Test5 = () => (

    static only

    ) // static and dynamic export const Test6 = ({ color }) => (

    static and dynamic

    ) // dynamic only export const Test7 = ({ color }) => (

    dynamic only

    ) // dynamic with scoped compound variable export const Test8 = ({ color }) => { if (color) { const innerProps = { color } return (

    dynamic with scoped compound variable

    ) } } // dynamic with compound variable export const Test9 = ({ color }) => { const innerProps = { color } return (

    dynamic with compound variable

    ) } const foo = 'red' // dynamic with constant variable export const Test10 = () => (

    dynamic with constant variable

    ) // dynamic with complex scope export const Test11 = ({ color }) => { const items = Array.from({ length: 5 }).map((item, i) => (
  • Item #{i + 1}
  • )) return
      {items}
    } ================================================ FILE: test/fixtures/class.js ================================================ export default class { render() { return (

    test

    ) } } ================================================ FILE: test/fixtures/component-attribute.js ================================================ const Test = () => (
    test
    ) ================================================ FILE: test/fixtures/conflicts.js ================================================ export const _JSXStyle = '_JSXStyle-literal' export default function() { return (

    test

    ) } ================================================ FILE: test/fixtures/css-tag-same-file.js ================================================ import css from 'styled-jsx/css' export default ({ children }) => (

    {children}

    ) const styles = css` p { color: red; } ` class Test extends React.Component { render() { return (

    {this.props.children}

    ) } } ================================================ FILE: test/fixtures/different-jsx-ids.js ================================================ const color = 'red' const otherColor = 'green' const A = () => (

    test

    ) const B = () => (

    test

    ) export default () => ( ) ================================================ FILE: test/fixtures/dynamic-element-class.js ================================================ export default class { render() { const Element = 'div' return (

    dynamic element

    ) } } const Element2 = 'div' export const Test2 = class { render() { return (

    dynamic element

    ) } } ================================================ FILE: test/fixtures/dynamic-element-external.js ================================================ import styles from './styles2' export default ({ level = 1 }) => { const Element = `h${level}` return (

    dynamic element

    ) } ================================================ FILE: test/fixtures/dynamic-element.js ================================================ export default ({ level = 1 }) => { const Element = `h${level}` return (

    dynamic element

    ) } export const TestLowerCase = ({ level = 1 }) => { const element = `h${level}` return (

    dynamic element

    ) } const Element2 = 'div' export const Test2 = () => { return (

    dynamic element

    ) } ================================================ FILE: test/fixtures/dynamic-this-value-in-arrow.js ================================================ import React, { Component } from 'react' export default class Index extends Component { static getInitialProps() { return { color: 'aquamarine' } } render() { return (
    {[1, 2].map(idx => (
    {[3, 4].map(idx2 => (
    {this.props.color}
    ))}
    ))} {[1, 2].map(idx => (
    {this.props.color}
    {this.props.color} hello there
    ))}
    ) } } ================================================ FILE: test/fixtures/expressions.js ================================================ const darken = c => c const color = 'red' const otherColor = 'green' const mediumScreen = '680px' const animationDuration = '200ms' const animationName = 'my-cool-animation' const obj = { display: 'block' } export default ({ display }) => (

    test

    ) ================================================ FILE: test/fixtures/external-stylesheet-global.js ================================================ import styles, { foo as styles3 } from './styles' const styles2 = require('./styles2') export default () => (

    test

    woot

    woot

    ) ================================================ FILE: test/fixtures/external-stylesheet-multi-line.js ================================================ import styles from './styles' export default () => (

    test

    ) ================================================ FILE: test/fixtures/external-stylesheet.js ================================================ import styles from './styles' const styles2 = require('./styles2') import { foo as styles3 } from './styles' export default () => (

    test

    woot

    woot
    ) export const Test = () => (

    test

    woot

    woot
    ) ================================================ FILE: test/fixtures/fragment.js ================================================ import React from 'react' export default () => ( <>

    Testing!!!

    Bar

    <>

    hello

    <>

    foo

    bar

    world

    ) function Component1() { return ( <>
    test
    ) } function Component2() { return (
    ) } ================================================ FILE: test/fixtures/global.js ================================================ const Test = () => (
    ) const Test2 = () => ( ) ================================================ FILE: test/fixtures/ignore.js ================================================ export default () => (

    test

    ) ================================================ FILE: test/fixtures/macro.js ================================================ import css, { resolve } from '../../test/helpers/babel-test.macro' const { className, styles } = resolve` div { color: red } ` const dynamicStyles = props => resolve` div { color: ${props.color} } ` const test = css.resolve` div { color: red } ` const dynamicStyles2 = props => css.resolve` div { color: ${props.color} } ` const ExampleComponent = props => { const { className, styles } = dynamicStyles(props) return (
    howdy {styles}
    ) } ================================================ FILE: test/fixtures/mixed-global-scoped.js ================================================ const Test = () => ( ) export default () => (

    test

    ) ================================================ FILE: test/fixtures/multiple-jsx.js ================================================ const attrs = { id: 'test' } const Test1 = () => (
    test
    ) const Test2 = () => test const Test3 = () => (
    test
    ) export default class { render() { return (

    test

    ) } } ================================================ FILE: test/fixtures/nested-style-tags.js ================================================ import styles from './styles' export default () => (
    test
    ) export const Test = () => (
    test {/* this should not be transpiled */}
    ) ================================================ FILE: test/fixtures/non-styled-jsx-style.js ================================================ export default () => (

    woot

    ) ================================================ FILE: test/fixtures/not-styled-jsx-tagged-templates.js ================================================ import css from 'hell' const color = 'red' const bar = css` div { font-size: 3em; } ` export const uh = bar export const foo = css` div { color: ${color}; } ` export default css` div { font-size: 3em; } p { color: ${color}; } ` const Title = styled.h1` color: red; font-size: 50px; ` const AnotherTitle = Title.extend` color: blue; ` export const Component = () => My page ================================================ FILE: test/fixtures/plugins/another-plugin.js ================================================ export default css => css.replace(/div/g, 'span') ================================================ FILE: test/fixtures/plugins/invalid-plugin.js ================================================ export default 'invalid' ================================================ FILE: test/fixtures/plugins/multiple-options.js ================================================ export default (css, settings) => { let { babel, ...s } = settings let { filename, ...b } = babel s.babel = b if (!filename) { throw new Error('filename should be defined') } return `.test { content: "${JSON.stringify(s)}"; }` } ================================================ FILE: test/fixtures/plugins/options.js ================================================ export default (css, settings) => settings.test ================================================ FILE: test/fixtures/plugins/plugin.js ================================================ export default css => `TEST (${css}) EOTEST` ================================================ FILE: test/fixtures/simple-fragment.js ================================================ export default () => ( <>

    This should throw in babel 6

    ) ================================================ FILE: test/fixtures/source-maps.js ================================================ export default () => (

    test

    woot

    ) ================================================ FILE: test/fixtures/stateless.js ================================================ export default () => (

    test

    woot

    woot

    ) ================================================ FILE: test/fixtures/styles-external-invalid.js ================================================ import css from 'styled-jsx/css' const color = 'red' export const foo = css` div { color: ${color}; } ` const props = { color: 'red ' } export default css` div { font-size: 3em; color: ${props.color}; } p { color: ${this.props.color}; } ` ================================================ FILE: test/fixtures/styles-external-invalid2.js ================================================ import css from 'styled-jsx/css' const color = 'red' export const foo = css` div { color: ${color}; } ` export default css` div { font-size: 3em; } p { color: ${props.color}; } ` ================================================ FILE: test/fixtures/styles.js ================================================ import css, { resolve, global } from 'styled-jsx/css' import colors, { size } from './constants' const color = 'red' const bar = css` div { font-size: 3em; } ` const baz = css.global` div { font-size: 3em; } ` const a = global` div { font-size: ${size}em; } ` export const uh = bar export const foo = css` div { color: ${color}; } ` css.resolve` div { color: ${colors.green.light}; } a { color: red } ` const b = resolve` div { color: ${colors.green.light}; } a { color: red } ` const dynamic = colors => { const b = resolve` div { color: ${colors.green.light}; } a { color: red } ` } export default css.resolve` div { font-size: 3em; } p { color: ${color}; } ` ================================================ FILE: test/fixtures/styles2.js ================================================ import css from 'styled-jsx/css' module.exports = css` div { font-size: 3em; } ` ================================================ FILE: test/fixtures/transform.css ================================================ html { background-image: linear-gradient( 0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8) ), url(/static/background.svg); } :global(p) { color: blue; } :global(p) { color: blue; } :global(p), a { color: blue; } :global(.foo + a) { color: red; } :global(body) { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; } p { color: red; } p { color: red; } * { color: blue; } [href='woot'] { color: red; } p a span { color: red; } p :global(span) { background: blue; } p a[title="'w ' ' t'"] { margin: auto; } p :global(span:not(.test)) { color: green; } p, h1 { color: blue; animation: hahaha 3s ease forwards infinite; animation-name: hahaha; animation-delay: 100ms; } p { animation: hahaha 1s, hehehe 2s; } p:hover { color: red; } p::before { color: red; } :hover { color: red; } ::before { color: red; } :hover p { color: red; } p + a { color: red; } p ~ a { color: red; } p > a { color: red; } @keyframes hahaha { from { top: 0; } to { top: 100; } } @keyframes hehehe { from { left: 0; } to { left: 100; } } @media (min-width: 500px) { .test { color: red; } } .test { /* test, test */ display: block; /* test */ } .inline-flex { display: inline-flex; } .flex { display: flex; } .test { box-shadow: 0 0 10px black, inset 0 0 5px black; } .test[title=','] { display: inline-block; } .test.is-status .test { color: red; } .a-selector:hover, .a-selector:focus { outline: none; } @media (min-width: 1px) and (max-width: 768px) { [class*='grid__col--'] { margin-top: 12px; margin-bottom: 12px; } } @media (max-width: 64em) { .test { margin-bottom: 1em; } @supports (-moz-appearance: none) and (display: contents) { .test { margin-bottom: 2rem; } } } ================================================ FILE: test/fixtures/whitespace.js ================================================ export default () => (

    test

    woot

    woot

    ) ================================================ FILE: test/fixtures/with-plugins.js ================================================ import styles from './styles' const color = 'red' export default () => (

    test

    ) ================================================ FILE: test/helpers/babel-test.macro.js ================================================ import { macro } from '../../src/babel' const m = macro() console.log('m', m) export default m ================================================ FILE: test/helpers/with-mock.js ================================================ export default function withMock(mockFn, testFn) { return t => { const cleanUp = mockFn(t) if (typeof cleanUp !== 'function') { throw new TypeError('mockFn should return a cleanup function') } testFn(t) cleanUp(t) } } export function withMockDocument(t) { const originalDocument = globalThis.document // We need to stub a document in order to simulate the meta tag globalThis.document = { querySelector(query) { t.is(query, 'meta[property="csp-nonce"]') return { getAttribute(attr) { t.is(attr, 'content') return 'test-nonce' } } } } return () => { globalThis.document = originalDocument } } ================================================ FILE: test/index.js ================================================ // Packages import test from 'ava' import React from 'react' 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], ...opts }) const transformSource = (src, opts = {}) => _transformSource(src.trim(), { plugins: [[plugin, opts]], ...opts }) test('handles dynamic `this` value inside of arrow function', async t => { const { code } = await transform( './fixtures/dynamic-this-value-in-arrow.js', { plugins: ['@babel/plugin-transform-arrow-functions', plugin] } ) t.snapshot(code) }) test('works with stateless', async t => { const { code } = await transform('./fixtures/stateless.js') t.snapshot(code) }) test('works with fragment', async t => { const { code } = await transform('./fixtures/fragment.js') t.snapshot(code) }) test('ignores whitespace around expression container', async t => { const { code } = await transform('./fixtures/whitespace.js') t.snapshot(code) }) test('works with class', async t => { const { code } = await transform('./fixtures/class.js') t.snapshot(code) }) test('ignores when attribute is absent', async t => { const { code } = await transform('./fixtures/absent.js') t.snapshot(code) }) test('works with global styles', async t => { const { code } = await transform('./fixtures/global.js') t.snapshot(code) }) test('generates source maps', async t => { const { code } = await transform('./fixtures/source-maps.js', { plugins: [[plugin, { sourceMaps: true }]] }) t.snapshot(code) }) test('mixed global and scoped', async t => { const { code } = await transform('./fixtures/mixed-global-scoped.js') t.snapshot(code) }) test('works with multiple jsx blocks', async t => { const { code } = await transform('./fixtures/multiple-jsx.js') t.snapshot(code) }) test('should not add the data-jsx attribute to components instances', async t => { const { code } = await transform('./fixtures/component-attribute.js') t.snapshot(code) }) test('works with expressions in template literals', async t => { const { code } = await transform('./fixtures/expressions.js') t.snapshot(code) }) test('should have different jsx ids', async t => { const { code } = await transform('./fixtures/different-jsx-ids.js') t.snapshot(code) }) test('works with non styled-jsx styles', async t => { const { code } = await transform('./fixtures/non-styled-jsx-style.js') t.snapshot(code) }) test('works with css tagged template literals in the same file', async t => { const { code } = await transform('./fixtures/css-tag-same-file.js') t.snapshot(code) }) test('works with dynamic element', async t => { const { code } = await transform('./fixtures/dynamic-element.js') t.snapshot(code) }) test('works with dynamic element in class', async t => { const { code } = await transform('./fixtures/dynamic-element-class.js') t.snapshot(code) }) test('works with existing identifier for _JSXStyle', async t => { const { code } = await transform('./fixtures/conflicts.js') t.snapshot(code) }) test('does not transpile nested style tags', async t => { const { message } = await t.throwsAsync(() => transform('./fixtures/nested-style-tags.js') ) t.regex(message, /detected nested style tag/i) }) test('works with exported jsx-style (CommonJS modules)', async t => { const { code } = await transformSource( 'module.exports = () =>

    ', { plugins: [plugin, '@babel/plugin-transform-modules-commonjs'] } ) t.snapshot(code) }) test('works with exported non-jsx style (CommonJS modules)', async t => { const { code } = await transformSource( 'module.exports = () =>

    ', { plugins: [plugin, '@babel/plugin-transform-modules-commonjs'] } ) t.snapshot(code) }) 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 => { function App() { const color = 'green' return React.createElement( 'div', null, React.createElement( JSXStyle, { id: 1 }, 'p { color: red }' ), React.createElement( JSXStyle, { id: 2 }, 'div { color: blue }' ), React.createElement( JSXStyle, { id: 3 }, `div { color: ${color} }` ) ) } // Expected CSS const expected = '' + '' + '' const registry = createStyleRegistry() const createApp = () => React.createElement(StyleRegistry, { registry }, React.createElement(App)) // Render using react ReactDOM.renderToString(createApp()) const html = ReactDOM.renderToStaticMarkup( React.createElement('head', null, flushToReact(registry)) ) t.is(html, `${expected}`) // Assert that memory is empty t.is(0, registry.cssRules().length) t.is('', flushToHTML(registry)) // Render to html again ReactDOM.renderToString(createApp()) t.is(expected, flushToHTML(registry)) // Assert that memory is empty t.is(0, flushToReact(registry).length) t.is('', flushToHTML(registry)) }) test('server rendering with nonce', t => { function App() { const color = 'green' return React.createElement( 'div', null, React.createElement( JSXStyle, { id: 1 }, 'p { color: red }' ), React.createElement( JSXStyle, { id: 2 }, 'div { color: blue }' ), React.createElement( JSXStyle, { id: 3 }, `div { color: ${color} }` ) ) } const registry = createStyleRegistry() const createApp = () => React.createElement(StyleRegistry, { registry }, React.createElement(App)) // Expected CSS const expected = '' + '' + '' // Render using react ReactDOM.renderToString(createApp()) const html = ReactDOM.renderToStaticMarkup( React.createElement( 'head', null, flushToReact(registry, { nonce: 'test-nonce' }) ) ) t.is(html, `${expected}`) // Assert that memory is empty t.is(0, flushToReact(registry, { nonce: 'test-nonce' }).length) t.is('', flushToHTML(registry, { nonce: 'test-nonce' })) // Render to html again ReactDOM.renderToString(createApp()) t.is(expected, flushToHTML(registry, { nonce: 'test-nonce' })) // Assert that memory is empty 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 => { function App() { return React.createElement( 'div', null, React.createElement( JSXStyle, { id: 1 }, ['p { color: red }', '.foo { color: hotpink }'] ) ) } const registry = createStyleRegistry() const createApp = () => React.createElement(StyleRegistry, { registry }, React.createElement(App)) ReactDOM.renderToString(createApp()) const html = ReactDOM.renderToStaticMarkup( React.createElement('head', null, flushToReact(registry)) ) const expected = '' t.is(html, `${expected}`) }) ================================================ FILE: test/index.ts ================================================ import {} from 'styled-jsx' ================================================ FILE: test/macro.js ================================================ // Packages import test from 'ava' // Ours import macros from 'babel-plugin-macros' import jsx from '@babel/plugin-syntax-jsx' import _transform, { transformSource as _transformSource } from './_transform' const transform = (file, opts = {}) => _transform(file, { plugins: [macros, jsx], ...opts }) const transformSource = (src, opts = {}) => _transformSource(src.trim(), { filename: './index.js', plugins: [macros, jsx], ...opts }) test('transpiles correctly', async t => { const { code } = await transform('./fixtures/macro.js') t.snapshot(code) }) test('throws when using the default export directly', async t => { const { message } = await t.throwsAsync(() => transformSource(` import css from './test/helpers/babel-test.macro' css\`div { color: red }\` `) ) t.regex(message, /can't use default import directly/i) }) test('throws when using the default export directly and it is not called css', async t => { const { message } = await t.throwsAsync(() => transformSource(` import foo from './test/helpers/babel-test.macro' foo\`div { color: red }\` `) ) t.regex(message, /can't use default import directly/i) }) test('throws when using the default export directly and it is not called resolve', async t => { const { message } = await t.throwsAsync(() => transformSource(` import resolve from './test/helpers/babel-test.macro' resolve\`div { color: red }\` `) ) t.regex(message, /can't use default import directly/i) }) test('throws when using an invalid method from the default export', async t => { const { message } = await t.throwsAsync(() => transformSource(` import css from './test/helpers/babel-test.macro' css.foo\`div { color: red }\` `) ) t.regex(message, /using an invalid tag/i) }) test('throws when using a named import different than resolve', async t => { const { message } = await t.throwsAsync(() => transformSource(` import { foo } from './test/helpers/babel-test.macro' foo\`div { color: red }\` `) ) t.regex(message, /imported an invalid named import/i) }) test('throws when using a named import as a member expression', async t => { const { message } = await t.throwsAsync(() => transformSource(` import { resolve } from './test/helpers/babel-test.macro' resolve.foo\`div { color: red }\` `) ) t.regex(message, /can't use named import/i) }) test('can alias the named import', async t => { const { code } = await transformSource(` import { resolve as foo } from './test/helpers/babel-test.macro' foo\`div { color: red }\` `) t.snapshot(code) }) test('injects JSXStyle for nested scope', async t => { const { code } = await transformSource(` import { resolve } from './test/helpers/babel-test.macro' function test() { resolve\`div { color: red }\` } `) t.snapshot(code) }) ================================================ FILE: test/plugins.js ================================================ // Packages import test from 'ava' // Ours import babelPlugin from '../src/babel' import babelTestPlugin from '../src/babel-test' import { combinePlugins } from '../src/_utils' import _transform from './_transform' import testPlugin1 from './fixtures/plugins/plugin' import testPlugin2 from './fixtures/plugins/another-plugin' const transform = (file, opts = {}) => _transform(file, { plugins: [ [ babelPlugin, { plugins: [require.resolve('./fixtures/plugins/another-plugin')] } ] ], ...opts }) test('combinePlugins returns an identity function when plugins is undefined', t => { const test = 'test' const plugins = combinePlugins() t.is(plugins(test), test) }) test('combinePlugins throws if plugins is not an array', t => { t.throws(() => { combinePlugins(2) }) }) test('combinePlugins throws if plugins is not an array of strings', t => { t.throws(() => { combinePlugins(['test', 2]) }) }) test('combinePlugins throws if loaded plugins are not functions', t => { t.throws(() => { combinePlugins([ require.resolve('./fixtures/plugins/plugin'), require.resolve('./fixtures/plugins/invalid-plugin') ]) }) }) test('combinePlugins works with a single plugin', t => { const plugins = combinePlugins([require.resolve('./fixtures/plugins/plugin')]) t.is(testPlugin1('test'), plugins('test')) }) test('combinePlugins works with options', t => { const expectedOption = 'my-test' const plugins = combinePlugins([ [ require.resolve('./fixtures/plugins/options'), { test: expectedOption } ] ]) t.is(plugins(''), expectedOption) }) test('combinePlugins applies plugins left to right', t => { const plugins = combinePlugins([ require.resolve('./fixtures/plugins/plugin'), require.resolve('./fixtures/plugins/another-plugin') ]) t.is(testPlugin2(testPlugin1('test')), plugins('test')) }) test('applies plugins', async t => { const { code } = await transform('./fixtures/with-plugins.js') t.snapshot(code) }) test('babel-test plugin strips jsx attribute', async t => { const { code } = await transform('./fixtures/with-plugins.js', { plugins: [babelTestPlugin] }) t.snapshot(code) }) test('passes options to plugins', async t => { const { code } = await transform('./fixtures/with-plugins.js', { plugins: [ [ babelPlugin, { plugins: [ [require.resolve('./fixtures/plugins/options'), { foo: true }], require.resolve('./fixtures/plugins/multiple-options'), [ require.resolve('./fixtures/plugins/multiple-options'), { foo: false } ] ], vendorPrefixes: false } ] ] }) t.snapshot(code) }) test('combinePlugins throws if passing an option called `babel`', t => { t.throws(() => { combinePlugins([['test', { babel: true }]]) }) }) test('combinePlugins memoizes calls', t => { const v1 = combinePlugins([require.resolve('./fixtures/plugins/plugin')]) const v2 = combinePlugins([require.resolve('./fixtures/plugins/plugin')]) t.is(v1('test div'), v2('test div')) const v3 = combinePlugins([ require.resolve('./fixtures/plugins/plugin'), require.resolve('./fixtures/plugins/another-plugin') ]) t.not(v2('test div'), v3('test div')) }) ================================================ FILE: test/snapshots/attribute.js.md ================================================ # Snapshot report for `test/attribute.js` The actual snapshot is saved in `attribute.js.snap`. Generated by [AVA](https://avajs.dev). ## rewrites className > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ export default (() => {␊ const Element = 'div';␊ return
    ␊ <_JSXStyle id={"2886504620"}>{"div.jsx-2886504620{color:red;}"}
    ;␊ });` ## generate attribute for mixed modes (global, static, dynamic) > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles from './styles';␊ ␊ const styles2 = require('./styles2'); // external only␊ ␊ ␊ export const Test1 = () =>

    external only

    ␊ <_JSXStyle id={styles.__hash}>{styles}␊ <_JSXStyle id={styles2.__hash}>{styles2}
    ; // external and static␊ ␊ export const Test2 = () =>

    external and static

    ␊ <_JSXStyle id={"2982525546"}>{"p.jsx-2982525546{color:red;}"}␊ <_JSXStyle id={styles.__hash}>{styles}
    ; // external and dynamic␊ ␊ export const Test3 = ({␊ color␊ }) =>

    external and dynamic

    ␊ <_JSXStyle id={"1947484460"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:${color};}\`}␊ <_JSXStyle id={styles.__hash}>{styles}
    ; // external, static and dynamic␊ ␊ export const Test4 = ({␊ color␊ }) =>

    external, static and dynamic

    ␊ <_JSXStyle id={"3190985107"}>{"p.jsx-3190985107{display:inline-block;}"}␊ <_JSXStyle id={"1336444426"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:${color};}\`}␊ <_JSXStyle id={styles.__hash}>{styles}
    ; // static only␊ ␊ export const Test5 = () =>

    static only

    ␊ <_JSXStyle id={"3190985107"}>{"p.jsx-1372669040{display:inline-block;}"}␊ <_JSXStyle id={"2982525546"}>{"p.jsx-1372669040{color:red;}"}
    ; // static and dynamic␊ ␊ export const Test6 = ({␊ color␊ }) =>

    static and dynamic

    ␊ <_JSXStyle id={"3190985107"}>{"p.jsx-3190985107{display:inline-block;}"}␊ <_JSXStyle id={"1336444426"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:${color};}\`}
    ; // dynamic only␊ ␊ export const Test7 = ({␊ color␊ }) =>

    dynamic only

    ␊ <_JSXStyle id={"1947484460"} dynamic={[color]}>{\`p.__jsx-style-dynamic-selector{color:${color};}\`}
    ; // dynamic with scoped compound variable␊ ␊ export const Test8 = ({␊ color␊ }) => {␊ if (color) {␊ const innerProps = {␊ color␊ };␊ return

    dynamic with scoped compound variable

    ␊ <_JSXStyle id={"1791723528"} dynamic={[innerProps.color]}>{\`p.__jsx-style-dynamic-selector{color:${innerProps.color};}\`}
    ;␊ }␊ }; // dynamic with compound variable␊ ␊ export const Test9 = ({␊ color␊ }) => {␊ const innerProps = {␊ color␊ };␊ return

    dynamic with compound variable

    ␊ <_JSXStyle id={"248922593"} dynamic={[innerProps.color]}>{\`p.__jsx-style-dynamic-selector{color:${innerProps.color};}\`}
    ;␊ };␊ const foo = 'red'; // dynamic with constant variable␊ ␊ export const Test10 = () =>

    dynamic with constant variable

    ␊ <_JSXStyle id={"461505126"}>{\`p.jsx-461505126{color:${foo};}\`}
    ; // dynamic with complex scope␊ ␊ export const Test11 = ({␊ color␊ }) => {␊ const items = Array.from({␊ length: 5␊ }).map((item, i) =>
  • ␊ <_JSXStyle id={"2172653867"} dynamic={[color]}>{\`.item.__jsx-style-dynamic-selector{color:${color};}\`}␊ Item #{i + 1}␊
  • );␊ return
      {items}
    ;␊ };` ================================================ FILE: test/snapshots/external.js.md ================================================ # Snapshot report for `test/external.js` The actual snapshot is saved in `external.js.snap`. Generated by [AVA](https://avajs.dev). ## transpiles external stylesheets > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import colors, { size } from './constants';␊ const color = 'red';␊ const bar = new String("div.jsx-2141779268{font-size:3em;}");␊ bar.__hash = "2141779268";␊ const baz = new String("div{font-size:3em;}");␊ baz.__hash = "2141779268";␊ const a = new String(\`div{font-size:${size}em;}\`);␊ a.__hash = "262929833";␊ export const uh = bar;␊ export const foo = new String(\`div.jsx-2433716433{color:${color};}\`);␊ foo.__hash = "2433716433";␊ ({␊ styles: <_JSXStyle id={"1329679275"}>{\`div.jsx-1329679275{color:${colors.green.light};}a.jsx-1329679275{color:red;}\`},␊ className: "jsx-1329679275"␊ });␊ const b = {␊ styles: <_JSXStyle id={"1329679275"}>{\`div.jsx-1329679275{color:${colors.green.light};}a.jsx-1329679275{color:red;}\`},␊ className: "jsx-1329679275"␊ };␊ ␊ const dynamic = colors => {␊ const b = {␊ styles: <_JSXStyle id={"3325296745"} dynamic={[colors.green.light]}>{\`div.__jsx-style-dynamic-selector{color:${colors.green.light};}a.__jsx-style-dynamic-selector{color:red;}\`},␊ className: _JSXStyle.dynamic([["3325296745", [colors.green.light]]])␊ };␊ };␊ ␊ export default {␊ styles: <_JSXStyle id={"3290112549"}>{\`div.jsx-3290112549{font-size:3em;}p.jsx-3290112549{color:${color};}\`},␊ className: "jsx-3290112549"␊ };` ## (optimized) transpiles external stylesheets > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import colors, { size } from './constants';␊ const color = 'red';␊ const bar = ["div.jsx-2141779268{font-size:3em;}"];␊ bar.__hash = "2141779268";␊ const baz = ["div{font-size:3em;}"];␊ baz.__hash = "2141779268";␊ const a = [\`div{font-size:${size}em;}\`];␊ a.__hash = "262929833";␊ export const uh = bar;␊ export const foo = [\`div.jsx-2433716433{color:${color};}\`];␊ foo.__hash = "2433716433";␊ ({␊ styles: <_JSXStyle id={"1329679275"}>{[\`div.jsx-1329679275{color:${colors.green.light};}\`, "a.jsx-1329679275{color:red;}"]},␊ className: "jsx-1329679275"␊ });␊ const b = {␊ styles: <_JSXStyle id={"1329679275"}>{[\`div.jsx-1329679275{color:${colors.green.light};}\`, "a.jsx-1329679275{color:red;}"]},␊ className: "jsx-1329679275"␊ };␊ ␊ const dynamic = colors => {␊ const b = {␊ styles: <_JSXStyle id={"3325296745"} dynamic={[colors.green.light]}>{[\`div.__jsx-style-dynamic-selector{color:${colors.green.light};}\`, "a.__jsx-style-dynamic-selector{color:red;}"]},␊ className: _JSXStyle.dynamic([["3325296745", [colors.green.light]]])␊ };␊ };␊ ␊ export default {␊ styles: <_JSXStyle id={"3290112549"}>{["div.jsx-3290112549{font-size:3em;}", \`p.jsx-3290112549{color:${color};}\`]},␊ className: "jsx-3290112549"␊ };` ## transpiles external stylesheets (CommonJS modules) > Snapshot 1 `const _defaultExport = new String("div.jsx-2141779268{font-size:3em;}");␊ ␊ _defaultExport.__hash = "2141779268";␊ module.exports = _defaultExport;` ## (optimized) transpiles external stylesheets (CommonJS modules) > Snapshot 1 `const _defaultExport = ["div.jsx-2141779268{font-size:3em;}"];␊ _defaultExport.__hash = "2141779268";␊ module.exports = _defaultExport;` ## does not transpile non-styled-jsx tagged teplate literals > Snapshot 1 `import css from 'hell';␊ const color = 'red';␊ const bar = css\`␊ div {␊ font-size: 3em;␊ }␊ \`;␊ export const uh = bar;␊ export const foo = css\`␊ div {␊ color: ${color};␊ }␊ \`;␊ export default css\`␊ div {␊ font-size: 3em;␊ }␊ p {␊ color: ${color};␊ }␊ \`;␊ const Title = styled.h1\`␊ color: red;␊ font-size: 50px;␊ \`;␊ const AnotherTitle = Title.extend\`␊ color: blue;␊ \`;␊ export const Component = () => My page;` ## use external stylesheets > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles from './styles';␊ ␊ const styles2 = require('./styles2');␊ ␊ import { foo as styles3 } from './styles';␊ export default (() =>

    test

    woot

    ␊ <_JSXStyle id={styles2.__hash}>{styles2}␊ <_JSXStyle id={styles3.__hash}>{styles3}
    woot
    ␊ <_JSXStyle id={"1646697228"}>{"p.jsx-1646697228{color:red;}div.jsx-1646697228{color:green;}"}␊ <_JSXStyle id={styles.__hash}>{styles}
    );␊ export const Test = () =>

    test

    woot

    ␊ <_JSXStyle id={styles3.__hash}>{styles3}
    woot
    ␊ <_JSXStyle id={"1646697228"}>{"p.jsx-1646697228{color:red;}div.jsx-1646697228{color:green;}"}
    ;` ## use external stylesheets (multi-line) > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles from './styles';␊ export default (() =>

    test

    ␊ <_JSXStyle id={styles.__hash}>{styles}
    );` ## use external stylesheets (global only) > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles, { foo as styles3 } from './styles';␊ ␊ const styles2 = require('./styles2');␊ ␊ export default (() =>

    test

    woot

    woot

    ␊ <_JSXStyle id={styles2.__hash}>{styles2}␊ <_JSXStyle id={styles3.__hash}>{styles3}␊ <_JSXStyle id={styles.__hash}>{styles}
    );` ## injects JSXStyle for nested scope > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ␊ function test() {␊ ({␊ styles: <_JSXStyle id={"2886504620"}>{"div.jsx-2886504620{color:red;}"},␊ className: "jsx-2886504620"␊ });␊ }` ## use external stylesheet and dynamic element > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles from './styles2';␊ export default (({␊ level = 1␊ }) => {␊ const Element = \`h${level}\`;␊ return

    dynamic element

    ␊ <_JSXStyle id={styles.__hash}>{styles}
    ;␊ });` ## Makes sure that style nodes are not re-used > Snapshot 1 `"use strict";␊ ␊ var _style = _interopRequireDefault(require("styled-jsx/style"));␊ ␊ var _App = _interopRequireDefault(require("./App.styles"));␊ ␊ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ ␊ function Test() {␊ return
    ␊ <_style.default id={_App.default.__hash}>{_App.default}
    ;␊ }` ## Make sure that it works with the new automatic transform > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import { jsx as _jsx } from "react/jsx-runtime";␊ const A = {␊ styles: /*#__PURE__*/_jsx(_JSXStyle, {␊ id: "2723508961",␊ children: "div.jsx-2723508961{color:green;}"␊ }),␊ className: "jsx-2723508961"␊ };␊ export default function IndexPage() {␊ return JSON.stringify(A);␊ }` ================================================ FILE: test/snapshots/index.js.md ================================================ # Snapshot report for `test/index.js` The actual snapshot is saved in `index.js.snap`. Generated by [AVA](https://avajs.dev). ## handles dynamic `this` value inside of arrow function > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import React, { Component } from 'react';␊ export default class Index extends Component {␊ static getInitialProps() {␊ return {␊ color: 'aquamarine'␊ };␊ }␊ ␊ render() {␊ var _this = this;␊ ␊ return
    ␊ {[1, 2].map(function (idx) {␊ return
    ␊ {[3, 4].map(function (idx2) {␊ return
    {_this.props.color}
    ;␊ })}␊
    ;␊ })}␊ {[1, 2].map(function (idx) {␊ return
    ␊ {_this.props.color}␊
    {_this.props.color} hello there
    ;␊ })}␊ <_JSXStyle id={"1028791522"} dynamic={[_this.props.color]}>{\`div.__jsx-style-dynamic-selector{background:${this.props.color};}\`}
    ;␊ }␊ ␊ }` ## works with stateless > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ export default (() =>

    test

    woot

    woot

    ␊ <_JSXStyle id={"2743241663"}>{"p.jsx-2743241663{color:red;}"}
    );` ## works with fragment > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import React from 'react';␊ export default (() => <>␊

    Testing!!!

    Bar

    ␊ <>␊

    hello

    ␊ <>␊

    foo

    bar

    ␊ ␊

    world

    ␊ ␊ <_JSXStyle id={"1712488456"}>{"p.jsx-1712488456{color:cyan;}.foo.jsx-1712488456{font-size:18px;color:hotpink;}#head.jsx-1712488456{-webkit-text-decoration:underline;text-decoration:underline;}"}␊ );␊ ␊ function Component1() {␊ return <>␊
    test
    ␊ ;␊ }␊ ␊ function Component2() {␊ return
    ␊ <_JSXStyle id={"4156136654"}>{"div.jsx-4156136654{color:red;}"}
    ;␊ }` ## ignores whitespace around expression container > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ export default (() =>

    test

    woot

    woot

    ␊ <_JSXStyle id={"2743241663"}>{"p.jsx-2743241663{color:red;}"}
    );` ## works with class > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ export default class {␊ render() {␊ return

    test

    ␊ <_JSXStyle id={"2101845350"}>{"p.jsx-2101845350{color:red;}"}
    ;␊ }␊ ␊ }` ## ignores when attribute is absent > Snapshot 1 `const a = () =>

    hi

    ;` ## works with global styles > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ␊ const Test = () =>
    ␊ <_JSXStyle id={"2381107078"}>{"body{color:red;}:hover{color:red;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-animation:foo 1s ease-out;animation:foo 1s ease-out;}div a{display:none;}[data-test]>div{color:red;}"}
    ;␊ ␊ const Test2 = () => <_JSXStyle id={"2743241663"}>{"p{color:red;}"};` ## generates source maps > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ export default (() =>

    test

    woot

    ␊ <_JSXStyle id={"2743241663"}>{"p.jsx-2743241663{color:red;}\\n/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNvdXJjZS1tYXBzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUlnQixBQUNjLFVBQUMiLCJmaWxlIjoic291cmNlLW1hcHMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCAoKSA9PiAoXG4gIDxkaXY+XG4gICAgPHA+dGVzdDwvcD5cbiAgICA8cD53b290PC9wPlxuICAgIDxzdHlsZSBqc3g+eydwIHsgY29sb3I6IHJlZCB9J308L3N0eWxlPlxuICA8L2Rpdj5cbilcbiJdfQ== */\\n/*@ sourceURL=source-maps.js */"}
    );` ## mixed global and scoped > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ␊ const Test = () => <_JSXStyle id={"2743241663"}>{"p{color:red;}"};␊ ␊ export default (() =>

    test

    ␊ <_JSXStyle id={"1061615554"}>{"body{background:red;}"}␊ <_JSXStyle id={"2743241663"}>{"p.jsx-3467702132{color:red;}"}
    );` ## works with multiple jsx blocks > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ const attrs = {␊ id: 'test'␊ };␊ ␊ const Test1 = () =>
    ␊ test␊ ␊ <_JSXStyle id={"2529315885"}>{"span.jsx-2529315885{color:red;}"}
    ;␊ ␊ const Test2 = () => test;␊ ␊ const Test3 = () =>
    test␊ <_JSXStyle id={"2529315885"}>{"span.jsx-2529315885{color:red;}"}
    ;␊ ␊ export default class {␊ render() {␊ return

    test

    ␊ <_JSXStyle id={"2101845350"}>{"p.jsx-2101845350{color:red;}"}
    ;␊ }␊ ␊ }` ## should not add the data-jsx attribute to components instances > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ␊ const Test = () =>
    test␊ <_JSXStyle id={"2529315885"}>{"span.jsx-2529315885{color:red;}"}
    ;` ## works with expressions in template literals > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ␊ const darken = c => c;␊ ␊ const color = 'red';␊ const otherColor = 'green';␊ const mediumScreen = '680px';␊ const animationDuration = '200ms';␊ const animationName = 'my-cool-animation';␊ const obj = {␊ display: 'block'␊ };␊ export default (({␊ display␊ }) =>

    test

    ␊ <_JSXStyle id={"1003380713"}>{\`p.${color}.jsx-3956171631{color:${otherColor};display:${obj.display};}\`}␊ <_JSXStyle id={"2743241663"}>{"p.jsx-3956171631{color:red;}"}␊ <_JSXStyle id={"602592955"}>{\`body{background:${color};}\`}␊ <_JSXStyle id={"602592955"}>{\`body{background:${color};}\`}␊ <_JSXStyle id={"128557999"}>{\`p.jsx-3956171631{color:${color};}\`}␊ <_JSXStyle id={"128557999"}>{\`p.jsx-3956171631{color:${color};}\`}␊ <_JSXStyle id={"2143443147"} dynamic={[darken(color)]}>{\`p.__jsx-style-dynamic-selector{color:${darken(color)};}\`}␊ <_JSXStyle id={"2381675492"} dynamic={[darken(color) + 2]}>{\`p.__jsx-style-dynamic-selector{color:${darken(color) + 2};}\`}␊ <_JSXStyle id={"309852217"}>{\`@media (min-width:${mediumScreen}){p.jsx-3956171631{color:green;}p.jsx-3956171631{color:${\`red\`};}}p.jsx-3956171631{color:red;}\`}␊ <_JSXStyle id={"2824547816"}>{\`p.jsx-3956171631{-webkit-animation-duration:${animationDuration};animation-duration:${animationDuration};}\`}␊ <_JSXStyle id={"417951176"}>{\`p.jsx-3956171631{-webkit-animation:${animationDuration} forwards ${animationName};animation:${animationDuration} forwards ${animationName};}div.jsx-3956171631{background:${color};}\`}␊ ␊ <_JSXStyle id={"3830663176"} dynamic={[display ? 'block' : 'none']}>{\`span.__jsx-style-dynamic-selector{display:${display ? 'block' : 'none'};}\`}␊ <_JSXStyle id={"1555166447"} dynamic={[display ? 'block' : 'none']}>{\`span.__jsx-style-dynamic-selector:before{display:${display ? 'block' : 'none'};content:'\\\`';}\`}
    );` ## should have different jsx ids > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ const color = 'red';␊ const otherColor = 'green';␊ ␊ const A = () =>

    test

    ␊ <_JSXStyle id={"128557999"}>{\`p.jsx-128557999{color:${color};}\`}
    ;␊ ␊ const B = () =>

    test

    ␊ <_JSXStyle id={"347939761"}>{\`p.jsx-347939761{color:${otherColor};}\`}
    ;␊ ␊ export default (() => );` ## works with non styled-jsx styles > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ export default (() =>

    woot

    ;` ================================================ FILE: test/snapshots/macro.js.md ================================================ # Snapshot report for `test/macro.js` The actual snapshot is saved in `macro.js.snap`. Generated by [AVA](https://avajs.dev). ## transpiles correctly > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ const {␊ className,␊ styles␊ } = {␊ styles: <_JSXStyle id={"2052294191"}>{"div.jsx-2052294191{color:red;}"},␊ className: "jsx-2052294191"␊ };␊ ␊ const dynamicStyles = props => ({␊ styles: <_JSXStyle id={"290194820"} dynamic={[props.color]}>{\`div.__jsx-style-dynamic-selector{color:${props.color};}\`},␊ className: _JSXStyle.dynamic([["290194820", [props.color]]])␊ });␊ ␊ const test = {␊ styles: <_JSXStyle id={"2052294191"}>{"div.jsx-2052294191{color:red;}"},␊ className: "jsx-2052294191"␊ };␊ ␊ const dynamicStyles2 = props => ({␊ styles: <_JSXStyle id={"290194820"} dynamic={[props.color]}>{\`div.__jsx-style-dynamic-selector{color:${props.color};}\`},␊ className: _JSXStyle.dynamic([["290194820", [props.color]]])␊ });␊ ␊ const ExampleComponent = props => {␊ const {␊ className,␊ styles␊ } = dynamicStyles(props);␊ return
    ␊ howdy␊ {styles}␊
    ;␊ };` ## can alias the named import > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ({␊ styles: <_JSXStyle id={"2886504620"}>{"div.jsx-2886504620{color:red;}"},␊ className: "jsx-2886504620"␊ });` ## injects JSXStyle for nested scope > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ ␊ function test() {␊ ({␊ styles: <_JSXStyle id={"2886504620"}>{"div.jsx-2886504620{color:red;}"},␊ className: "jsx-2886504620"␊ });␊ }` ================================================ FILE: test/snapshots/plugins.js.md ================================================ # Snapshot report for `test/plugins.js` The actual snapshot is saved in `plugins.js.snap`. Generated by [AVA](https://avajs.dev). ## applies plugins > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles from './styles';␊ const color = 'red';␊ export default (() =>

    test

    ␊ <_JSXStyle id={"2799750516"} dynamic={[color, otherColor]}>{\`span.${color}.__jsx-style-dynamic-selector{color:${otherColor};}\`}␊ <_JSXStyle id={styles.__hash}>{styles}
    );` ## babel-test plugin strips jsx attribute > Snapshot 1 `import styles from './styles';␊ const color = 'red';␊ export default (() =>

    test

    );` ## passes options to plugins > Snapshot 1 `import _JSXStyle from "styled-jsx/style";␊ import styles from './styles';␊ const color = 'red';␊ export default (() =>

    test

    ␊ <_JSXStyle id={"2799750516"} dynamic={[color, otherColor]}>{".test.__jsx-style-dynamic-selector{content:\\"{\\"foo\\":false,\\"babel\\":{\\"location\\":{\\"start\\":{\\"line\\":7,\\"column\\":16,\\"index\\":114},\\"end\\":{\\"line\\":11,\\"column\\":5,\\"index\\":180}},\\"vendorPrefixes\\":false,\\"sourceMaps\\":false,\\"isGlobal\\":false}}\\";}"}␊ <_JSXStyle id={styles.__hash}>{styles}
    );` ================================================ FILE: test/snapshots/styles.js.md ================================================ # Snapshot report for `test/styles.js` The actual snapshot is saved in `styles.js.snap`. Generated by [AVA](https://avajs.dev). ## transpile styles with attributes > Snapshot 1 'html.jsx-123{background-image:linear-gradient( 0deg, rgba(255,255,255,0.8), rgba(255,255,255,0.8) ), url(/static/background.svg);}p{color:blue;}p{color:blue;}p,a.jsx-123{color:blue;}.foo + a{color:red;}body{font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Helvetica,Arial, sans-serif;}p.jsx-123{color:red;}p.jsx-123{color:red;}*.jsx-123{color:blue;}[href=\'woot\'].jsx-123{color:red;}p.jsx-123 a.jsx-123 span.jsx-123{color:red;}p.jsx-123 span{background:blue;}p.jsx-123 a[title="\'w \' \' t\'"].jsx-123{margin:auto;}p.jsx-123 span:not(.test){color:green;}p.jsx-123,h1.jsx-123{color:blue;-webkit-animation:hahaha-jsx-123 3s ease forwards infinite;animation:hahaha-jsx-123 3s ease forwards infinite;-webkit-animation-name:hahaha-jsx-123;animation-name:hahaha-jsx-123;-webkit-animation-delay:100ms;animation-delay:100ms;}p.jsx-123{-webkit-animation:hahaha-jsx-123 1s,hehehe-jsx-123 2s;animation:hahaha-jsx-123 1s,hehehe-jsx-123 2s;}p.jsx-123:hover{color:red;}p.jsx-123::before{color:red;}.jsx-123:hover{color:red;}.jsx-123::before{color:red;}.jsx-123:hover p.jsx-123{color:red;}p.jsx-123+a.jsx-123{color:red;}p.jsx-123~a.jsx-123{color:red;}p.jsx-123>a.jsx-123{color:red;}@-webkit-keyframes hahaha-jsx-123{from{top:0;}to{top:100;}}@keyframes hahaha-jsx-123{from{top:0;}to{top:100;}}@-webkit-keyframes hehehe-jsx-123{from{left:0;}to{left:100;}}@keyframes hehehe-jsx-123{from{left:0;}to{left:100;}}@media (min-width:500px){.test.jsx-123{color:red;}}.test.jsx-123{display:block;}.inline-flex.jsx-123{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;}.flex.jsx-123{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.test.jsx-123{box-shadow:0 0 10px black,inset 0 0 5px black;}.test[title=\',\'].jsx-123{display:inline-block;}.test.is-status.jsx-123 .test.jsx-123{color:red;}.a-selector.jsx-123:hover,.a-selector.jsx-123:focus{outline:none;}@media (min-width:1px) and (max-width:768px){[class*=\'grid__col--\'].jsx-123{margin-top:12px;margin-bottom:12px;}}@media (max-width:64em){.test.jsx-123{margin-bottom:1em;}@supports (-moz-appearance:none) and (display:contents){.test.jsx-123{margin-bottom:2rem;}}}' ================================================ FILE: test/styles.js ================================================ // Packages import test from 'ava' // Ours import transform from '../src/lib/style-transform' import read from './_read' test('transpile styles with attributes', async t => { const src = await read('./fixtures/transform.css') t.snapshot(transform('.jsx-123', src)) }) test('throws when using nesting', t => { const fixtures = [ `div { &:hover { color: red } }`, `div { color: green; &:hover { color: red } }`, `:hover { div { color: red } }`, `@media all { div { &:hover { color: red } } }`, `* { div { color: red } &.test { color: red; } }`, `span {} .test { color: red; div& { color: red; } }` ] fixtures.forEach(fixture => { t.throws(() => transform('', fixture)) t.throws(() => transform('.jsx-123', fixture)) }) }) test("doesn't throw when using at-rules", t => { const fixtures = [ '@media (min-width: 480px) { div { color: red } }', `span {} @media (min-width: 480px) { div { color: red } }`, `@media (min-width: 480px) { div { color: red } } span {}`, `:hover {} @media (min-width: 480px) { div { color: red } }`, `:hover { color: green } @media (min-width: 480px) { div { color: red } }`, `@media (min-width: 480px) { div {} }`, `@keyframes foo { 0% { opacity: 0 } 100% { opacity: 1} } `, // Line with one space before @rule `div { color: red; } @media screen and (min-width: 480px) { div { color: red; } } `, // Line with one tab before @rule `div { color: red; } @media screen and (min-width: 480px) { div { color: red; } } ` ] fixtures.forEach(fixture => { t.notThrows(() => transform('', fixture)) t.notThrows(() => transform('.jsx-123', fixture)) }) }) test('splits rules for `optimizeForSpeed`', t => { function assert(input, expected, prefix = '') { t.deepEqual(transform(prefix, input, { splitRules: true }), expected) } assert('div { color: red }', ['div{color:red;}']) assert('div { color: red } .p { color: red }', [ 'div{color:red;}', '.p{color:red;}' ]) assert('div, span { color: red } a > .p { color: red }', [ 'div,span{color:red;}', 'a>.p{color:red;}' ]) assert( '@media (min-width: 400px) { div, span { color: red } } a > .p { color: red }', ['@media (min-width:400px){div,span{color:red;}}', 'a>.p{color:red;}'] ) assert( '@media (min-width: 400px) { div { color: red } span { color: red } } a > .p { color: red }', [ '@media (min-width:400px){div{color:red;}span{color:red;}}', 'a>.p{color:red;}' ] ) assert( `@media (min-width: 1px) and (max-width: 768px) { [class*='test__test--'] { color: red; } }`, [ `@media (min-width:1px) and (max-width:768px){[class*='test__test--']{color:red;}}` ] ) assert( 'span { color: red } @font-face { font-family: test; src: url(test.woff); } div { color: red }', [ '@font-face{font-family:test;src:url(test.woff);}', 'span{color:red;}', 'div{color:red;}' ] ) assert('@charset "UTF-8"', ['@charset "UTF-8";']) assert('@import "./test.css"', ['@import "./test.css";']) assert( ` @keyframes test { 0% { opacity: 0 } 100% { opacity: 1 } } `, [ '@-webkit-keyframes test{0%{opacity:0;}100%{opacity:1;}}', '@keyframes test{0%{opacity:0;}100%{opacity:1;}}' ] ) assert( ` @supports (display: flex) { div { display: flex; } } `, [ '@supports (display:-webkit-box) or (display:-webkit-flex) or (display:-ms-flexbox) or (display:flex){div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}}' ] ) assert( ` @import "./test.css"; @supports (display: flex) { div { display: flex; } } div { color: red } a, div { color: red } @import "./test.css"; @media (min-width: 400px) { div, span { color: red } } `, [ '@import "./test.css";', '@import "./test.css";', '@supports (display:-webkit-box) or (display:-webkit-flex) or (display:-ms-flexbox) or (display:flex){div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}}', 'div{color:red;}', 'a,div{color:red;}', '@media (min-width:400px){div,span{color:red;}}' ] ) assert('@namespace url(http://www.w3.org/1999/xhtml)', [ '@namespace url(http://www.w3.org/1999/xhtml);' ]) assert('@namespace svg url(http://www.w3.org/2000/svg)', [ '@namespace svg url(http://www.w3.org/2000/svg);' ]) assert('@page :first { margin: 1in; }', ['@page :first{margin:1in;}']) assert( ` div { animation: fade-in ease-in 1; animation-fill-mode: forwards; animation-duration: 500ms; opacity: 0; } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } `, [ 'div.jsx-123{-webkit-animation:fade-in-jsx-123 ease-in 1;animation:fade-in-jsx-123 ease-in 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-duration:500ms;animation-duration:500ms;opacity:0;}', '@-webkit-keyframes fade-in-jsx-123{from{opacity:0;}to{opacity:1;}}', '@keyframes fade-in-jsx-123{from{opacity:0;}to{opacity:1;}}' ], '.jsx-123' ) assert( ` div { animation: fade-in ease-in 1; animation-fill-mode: forwards; animation-duration: 500ms; opacity: 0; } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } `, [ 'div{-webkit-animation:fade-in ease-in 1;animation:fade-in ease-in 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-duration:500ms;animation-duration:500ms;opacity:0;}', '@-webkit-keyframes fade-in{from{opacity:0;}to{opacity:1;}}', '@keyframes fade-in{from{opacity:0;}to{opacity:1;}}' ] ) assert( `div { color: red } ::placeholder { color: green }`, [ 'div.jsx-123{color:red;}', '.jsx-123::-webkit-input-placeholder{color:green;}', '.jsx-123::-moz-placeholder{color:green;}', '.jsx-123:-ms-input-placeholder{color:green;}', '.jsx-123::placeholder{color:green;}' ], '.jsx-123' ) }) ================================================ FILE: test/stylesheet-registry.js ================================================ // Packages import test from 'ava' // Ours 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 }) { const registry = new StyleSheetRegistry({ styleSheet: makeSheet(options), ...options }) registry.selectFromServer = () => ({}) return registry } const cssRule = 'div { color: red }' const cssRuleAlt = 'p { color: red }' // registry.add test( 'add', withMock(withMockDocument, t => { const options = [ { optimizeForSpeed: true, isBrowser: true }, { optimizeForSpeed: false, isBrowser: true }, { optimizeForSpeed: true, isBrowser: false }, { optimizeForSpeed: false, isBrowser: false } ] options.forEach(options => { if (options.isBrowser) { globalThis.window = globalThis } const registry = makeRegistry(options) registry.add({ id: '123', children: options.optimizeForSpeed ? [cssRule] : cssRule }) t.deepEqual(registry.cssRules(), [['jsx-123', cssRule]]) // Dedupe registry.add({ id: '123', children: options.optimizeForSpeed ? [cssRule] : cssRule }) t.deepEqual(registry.cssRules(), [['jsx-123', cssRule]]) registry.add({ id: '345', children: options.optimizeForSpeed ? [cssRule] : cssRule }) t.deepEqual(registry.cssRules(), [ ['jsx-123', cssRule], ['jsx-345', cssRule] ]) if (options.optimizeForSpeed) { registry.add({ id: '456', children: [cssRule, cssRuleAlt] }) t.deepEqual(registry.cssRules(), [ ['jsx-123', cssRule], ['jsx-345', cssRule], ['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 registry.add({ id: '123', children: [cssRule] }) // Insert an invalid rule registry.add({ id: '456', children: [invalidRules[0]] }) // Insert another valid rule registry.add({ id: '678', children: [cssRule] }) t.deepEqual(registry.cssRules(), [ ['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 registry.add({ id: '123', children: [cssRule] }) t.notThrows(() => { // Insert an invalid rule registry.add({ id: '456', children: [invalidRules[0]] }) }) 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 }) registry.add({ id: '123', children: [ 'div.__jsx-style-dynamic-selector { color: red }' ], dynamic: ['red'] }) t.deepEqual(registry.cssRules(), [ [ 'jsx-1871671996', 'div.jsx-1871671996 { color: red<\\/style> }' ] ]) }) test('add - nonce is properly fetched from meta tag', t => { const originalDocument = globalThis.document // We need to stub a document in order to simulate the meta tag globalThis.document = { querySelector(query) { t.is(query, 'meta[property="csp-nonce"]') return { getAttribute(attr) { t.is(attr, 'content') return 'test-nonce' } } } } globalThis.window = globalThis const registry = makeRegistry() registry.add({ id: '123', children: [cssRule] }) t.is(registry._sheet._nonce, 'test-nonce') globalThis.document = originalDocument delete globalThis.window }) // registry.remove test( 'remove', withMock(withMockDocument, t => { const options = [ { optimizeForSpeed: true, isBrowser: true }, { optimizeForSpeed: false, isBrowser: true }, { optimizeForSpeed: true, isBrowser: false }, { optimizeForSpeed: false, isBrowser: false } ] options.forEach(options => { if (options.isBrowser) { globalThis.window = globalThis } const registry = makeRegistry(options) registry.add({ id: '123', children: options.optimizeForSpeed ? [cssRule] : cssRule }) registry.add({ id: '345', children: options.optimizeForSpeed ? [cssRuleAlt] : cssRuleAlt }) registry.remove({ id: '123' }) t.deepEqual(registry.cssRules(), [['jsx-345', cssRuleAlt]]) // Add a duplicate registry.add({ id: '345', children: options.optimizeForSpeed ? [cssRuleAlt] : cssRuleAlt }) // and remove it registry.remove({ id: '345' }) // Still in the registry t.deepEqual(registry.cssRules(), [['jsx-345', cssRuleAlt]]) // remove again registry.remove({ id: '345' }) // now the registry should be empty t.deepEqual(registry.cssRules(), []) if (options.isBrowser) { delete globalThis.window } }) }) ) // registry.update test( 'update', withMock(withMockDocument, t => { const options = [ { optimizeForSpeed: true, isBrowser: true }, { optimizeForSpeed: false, isBrowser: true }, { optimizeForSpeed: true, isBrowser: false }, { optimizeForSpeed: false, isBrowser: false } ] options.forEach(options => { if (options.isBrowser) { globalThis.window = globalThis } const registry = makeRegistry(options) registry.add({ id: '123', children: options.optimizeForSpeed ? [cssRule] : cssRule }) registry.add({ id: '123', children: options.optimizeForSpeed ? [cssRule] : cssRule }) registry.update( { id: '123' }, { id: '345', children: options.optimizeForSpeed ? [cssRuleAlt] : cssRuleAlt } ) // Doesn't remove when there are multiple instances of 123 t.deepEqual(registry.cssRules(), [ ['jsx-123', cssRule], ['jsx-345', cssRuleAlt] ]) registry.remove({ id: '345' }) t.deepEqual(registry.cssRules(), [['jsx-123', cssRule]]) // Update again registry.update( { id: '123' }, { id: '345', children: options.optimizeForSpeed ? [cssRuleAlt] : cssRuleAlt } ) // 123 replaced with 345 t.deepEqual(registry.cssRules(), [['jsx-345', cssRuleAlt]]) if (options.isBrowser) { delete globalThis.window } }) }) ) // createComputeId test( 'createComputeId', withMock(withMockDocument, t => { // without props t.is(computeId('123'), 'jsx-123') // with props t.is(computeId('123', ['test', 3, 'test']), 'jsx-1172888331') }) ) // createComputeSelector test( 'createComputeSelector', withMock(withMockDocument, t => { t.is( computeSelector( 'jsx-123', '.test {} .__jsx-style-dynamic-selector { color: red } .__jsx-style-dynamic-selector { color: red }' ), '.test {} .jsx-123 { color: red } .jsx-123 { color: red }' ) }) ) // getIdAndRules test( 'getIdAndRules', withMock(withMockDocument, t => { const utilRegistry = makeRegistry() // simple t.deepEqual( utilRegistry.getIdAndRules({ id: '123', children: '.test {} .jsx-123 { color: red } .jsx-123 { color: red }' }), { styleId: 'jsx-123', rules: ['.test {} .jsx-123 { color: red } .jsx-123 { color: red }'] } ) // dynamic t.deepEqual( utilRegistry.getIdAndRules({ id: '123', children: '.test {} .__jsx-style-dynamic-selector { color: red } .__jsx-style-dynamic-selector { color: red }', dynamic: ['test', 3, 'test'] }), { styleId: 'jsx-1172888331', rules: [ '.test {} .jsx-1172888331 { color: red } .jsx-1172888331 { color: red }' ] } ) // dynamic, css array t.deepEqual( utilRegistry.getIdAndRules({ id: '123', children: [ '.test {}', '.__jsx-style-dynamic-selector { color: red }', '.__jsx-style-dynamic-selector { color: red }' ], dynamic: ['test', 3, 'test'] }), { styleId: 'jsx-1172888331', rules: [ '.test {}', '.jsx-1172888331 { color: red }', '.jsx-1172888331 { color: red }' ] } ) }) ) ================================================ FILE: test/stylesheet.js ================================================ // Packages import test from 'ava' // Ours import StyleSheet from '../src/lib/stylesheet' import withMock, { withMockDocument } from './helpers/with-mock' export const invalidRules = ['invalid rule'] export default function makeSheet(options = { optimizeForSpeed: true }) { const sheet = new StyleSheet(options) // mocks sheet.makeStyleTag = function(name, cssString) { const cssRules = cssString ? [{ cssText: cssString }] : [] const tag = { sheet: { cssRules, insertRule: (rule, index) => { if (invalidRules.includes(rule)) { throw new Error('invalid rule') } if (typeof index === 'number') { cssRules[index] = { cssText: rule } } else { cssRules.push({ cssText: rule }) } return index }, deleteRule: index => { if (options.optimizeForSpeed) { cssRules[index] = { cssText: `#${name}-deleted-rule____{}` } } else { cssRules[index] = null } }, replaceRule: (index, rule) => { cssRules[index] = { cssText: rule } } }, parentNode: { removeChild: () => {} } } let textContent = cssString Object.defineProperty(tag, 'textContent', { get: () => textContent, set: text => { textContent = text cssRules.length = 0 cssRules.push({ cssText: text }) } }) return tag } return sheet } // sheet.setOptimizeForSpeed test( 'can change optimizeForSpeed only when the stylesheet is empty', withMock(withMockDocument, t => { globalThis.window = globalThis const sheet = makeSheet() sheet.inject() t.notThrows(() => { sheet.setOptimizeForSpeed(true) }) sheet.insertRule('div { color: red }') t.throws(() => { sheet.setOptimizeForSpeed(false) }) sheet.flush() t.notThrows(() => { sheet.setOptimizeForSpeed(false) }) delete globalThis.window }) ) // sheet.insertRule test( 'insertRule', withMock(withMockDocument, t => { const options = [ { optimizeForSpeed: true, isBrowser: true }, { optimizeForSpeed: false, isBrowser: true }, { optimizeForSpeed: true, isBrowser: false } ] options.forEach(options => { if (options.isBrowser) { globalThis.window = globalThis } const sheet = makeSheet(options) sheet.inject() sheet.insertRule('div { color: red }') t.deepEqual(sheet.cssRules(), [{ cssText: 'div { color: red }' }]) sheet.insertRule('div { color: green }') t.deepEqual(sheet.cssRules(), [ { cssText: 'div { color: red }' }, { cssText: 'div { color: green }' } ]) if (options.isBrowser) { delete globalThis.window } }) }) ) test( 'insertRule - returns the rule index', withMock(withMockDocument, t => { globalThis.window = globalThis const sheet = makeSheet() sheet.inject() let i = sheet.insertRule('div { color: red }') t.is(i, 0) i = sheet.insertRule('div { color: red }') t.is(i, 1) delete globalThis.window }) ) test( 'insertRule - handles invalid rules and returns -1 as index', withMock(withMockDocument, t => { globalThis.window = globalThis const sheet = makeSheet() sheet.inject() const i = sheet.insertRule(invalidRules[0]) t.is(i, -1) delete globalThis.window }) ) test( 'insertRule - does not fail when the css is a String object', withMock(withMockDocument, t => { globalThis.window = globalThis const sheet = makeSheet() sheet.inject() /* eslint-disable unicorn/new-for-builtins,no-new-wrappers */ sheet.insertRule(new String('div { color: red }')) t.deepEqual(sheet.cssRules(), [ { cssText: new String('div { color: red }') } ]) delete globalThis.window /* eslint-enable */ }) ) // sheet.deleteRule test( 'deleteRule', withMock(withMockDocument, t => { const options = [ { optimizeForSpeed: true, isBrowser: true }, { optimizeForSpeed: false, isBrowser: true }, { optimizeForSpeed: true, isBrowser: false }, { optimizeForSpeed: false, isBrowser: false } ] options.forEach(options => { if (options.isBrowser) { globalThis.window = globalThis } const sheet = makeSheet(options) sheet.inject() sheet.insertRule('div { color: red }') sheet.insertRule('div { color: green }') const rulesCount = sheet.length sheet.deleteRule(1) // When deleting we replace rules with placeholders to keep the indices stable. t.is(sheet.length, rulesCount) t.deepEqual(sheet.cssRules(), [{ cssText: 'div { color: red }' }, null]) if (options.isBrowser) { delete globalThis.window } }) }) ) test( 'deleteRule - does not throw when the rule at index does not exist', withMock(withMockDocument, t => { globalThis.window = globalThis const sheet = makeSheet() sheet.inject() t.notThrows(() => { sheet.deleteRule(sheet.length + 1) }) delete globalThis.window }) ) // sheet.replaceRule test( 'replaceRule', withMock(withMockDocument, t => { const options = [ { optimizeForSpeed: true, isBrowser: true }, { optimizeForSpeed: false, isBrowser: true }, { optimizeForSpeed: true, isBrowser: false }, { optimizeForSpeed: false, isBrowser: false } ] options.forEach(options => { if (options.isBrowser) { globalThis.window = globalThis } const sheet = makeSheet(options) sheet.inject() const index = sheet.insertRule('div { color: red }') sheet.replaceRule(index, 'p { color: hotpink }') t.deepEqual(sheet.cssRules(), [{ cssText: 'p { color: hotpink }' }]) if (options.isBrowser) { delete globalThis.window } }) }) ) test( 'replaceRule - handles invalid rules gracefully', withMock(withMockDocument, t => { globalThis.window = globalThis const sheet = makeSheet() sheet.inject() // Insert two rules sheet.insertRule('div { color: red }') const index = sheet.insertRule('div { color: red }') // Replace the latter with an invalid rule const i = sheet.replaceRule(index, invalidRules[0]) t.is(i, index) t.is(sheet.length, 2) // Even though replacement (insertion) failed deletion succeeded // therefore the lib must insert a delete placeholder which resolves to `null` // when `cssRules()` is called. t.deepEqual(sheet.cssRules(), [{ cssText: 'div { color: red }' }, null]) delete globalThis.window }) ) ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ /* Projects */ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ "module": "commonjs" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {} /* Specify a set of entries that re-map imports to additional lookup locations. */, // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ "types": [] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ // "resolveJsonModule": true, /* Enable importing .json files. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ // "newLine": "crlf", /* Set the newline character for emitting files. */ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": false /* Skip type checking all .d.ts files. */ }, } ================================================ FILE: webpack.js ================================================ module.exports = { loader: require.resolve('./dist/webpack') }