main ce798e3d21f8 cached
24 files
46.6 KB
12.7k tokens
30 symbols
1 requests
Download .txt
Repository: sergeybekrin/styled-email-components
Branch: main
Commit: ce798e3d21f8
Files: 24
Total size: 46.6 KB

Directory structure:
gitextract_4t0ert70/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .gitmodules
├── .vscode/
│   └── settings.json
├── LICENSE
├── babel.config.js
├── jest.config.js
├── package.json
├── readme.md
├── rollup.config.js
├── src/
│   ├── css-to-style.js
│   ├── index.js
│   ├── inline-style.js
│   ├── is-vml-tag.js
│   ├── styled-email-component.js
│   ├── stylesheet.js
│   └── xhtml-elements.js
├── styled-email-components.d.ts
├── test/
│   ├── namespace.spec.js
│   ├── specific.spec.js
│   ├── styled.spec.js
│   ├── theme.spec.js
│   └── typings.spec.tsx
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/test.yml
================================================
name: Test

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: recursive
      - uses: actions/setup-node@v2
        with:
          node-version: 14
      - run: yarn
      - run: yarn build
      - run: yarn test


================================================
FILE: .gitignore
================================================
node_modules/
dist/
*.log

================================================
FILE: .gitmodules
================================================
[submodule "styled-components"]
	path = vendor/styled-components
	url = https://github.com/styled-components/styled-components.git
	branch = legacy-v5


================================================
FILE: .vscode/settings.json
================================================
{
  "cSpell.words": [
    "camelcase"
  ]
}

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Sergey Bekrin

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: babel.config.js
================================================
module.exports = {
  presets: ["./vendor/styled-components/babel-preset"],
  plugins: [
    // Replace `styled-components/src/*` imports with relative `./vendor/styled-components/*` to let rollup bundle the original sources
    [
      "babel-plugin-module-resolver",
      {
        alias: {
          "styled-components/src":
            "./vendor/styled-components/packages/styled-components/src",
        },
      },
    ],
  ],
};


================================================
FILE: jest.config.js
================================================
module.exports = {
  testPathIgnorePatterns: ["/node_modules/", "/vendor/"],
  transformIgnorePatterns: ["/node_modules/(?!styled-components).+\\.js$"],
  setupFiles: ['./vendor/styled-components/packages/styled-components/src/test/globals.js']
} 

================================================
FILE: package.json
================================================
{
  "name": "styled-email-components",
  "version": "5.0.3",
  "description": "💌 styled-components for emails",
  "repository": "sbekrin/styled-email-components",
  "author": "Sergey Bekrin <sergey@bekrin.me>",
  "license": "MIT",
  "main": "dist/styled-email-components.cjs.js",
  "jsnext:main": "dist/styled-email-components.esm.js",
  "module": "dist/styled-email-components.esm.js",
  "browser": {
    "./dist/styled-email-components.esm.js": "./dist/styled-email-components.browser.esm.js",
    "./dist/styled-email-components.cjs.js": "./dist/styled-email-components.browser.cjs.js"
  },
  "files": [
    "dist",
    "styled-email-components.d.ts"
  ],
  "types": "./styled-email-components.d.ts",
  "scripts": {
    "prepare": "yarn --cwd vendor/styled-components/packages/styled-components generateErrors",
    "test": "jest",
    "build": "rollup -c"
  },
  "dependencies": {
    "@emotion/is-prop-valid": "1.1.0",
    "@emotion/stylis": "0.8.5",
    "@emotion/unitless": "0.7.5",
    "color-shorthand-hex-to-six-digit": "3.0.7",
    "css-shorthand-expand": "1.2.0",
    "hoist-non-react-statics": "3.3.2",
    "lodash.camelcase": "4.3.0",
    "postcss-safe-parser": "5.0.2",
    "shallowequal": "1.1.0"
  },
  "devDependencies": {
    "@babel/cli": "7.0.0",
    "@babel/core": "7.0.0",
    "@babel/plugin-external-helpers": "7.0.0",
    "@babel/plugin-proposal-class-properties": "7.0.0",
    "@babel/plugin-proposal-object-rest-spread": "7.0.0",
    "@babel/preset-env": "7.0.0",
    "@babel/preset-flow": "7.12.13",
    "@babel/preset-react": "7.0.0",
    "@types/jest": "26.0.22",
    "babel-jest": "26.5.2",
    "babel-plugin-add-module-exports": "1.0.2",
    "babel-plugin-module-resolver": "4.1.0",
    "babel-plugin-tester": "10.0.0",
    "babel-plugin-transform-react-remove-prop-types": "0.4.24",
    "jest": "26.6.3",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-is": "17.0.1",
    "react-test-renderer": "17.0.1",
    "rollup": "1.13.1",
    "rollup-plugin-babel": "4.3.2",
    "rollup-plugin-commonjs": "10.0.0",
    "rollup-plugin-flow": "github:probablyup/rollup-plugin-flow#breaking-update-flow-remove-types",
    "rollup-plugin-json": "4.0.0",
    "rollup-plugin-node-resolve": "5.0.1",
    "rollup-plugin-replace": "2.2.0",
    "rollup-plugin-sourcemaps": "0.4.2",
    "rollup-plugin-terser": "5.0.0",
    "styled-components": "file:./vendor/styled-components/packages/styled-components"
  }
}


================================================
FILE: readme.md
================================================
# 💌 styled-email-components

[![npm Version](https://img.shields.io/npm/v/styled-email-components.svg)](https://www.npmjs.com/package/styled-email-components)
[![Build Status](https://img.shields.io/travis/sbekrin/styled-email-components.svg)](https://travis-ci.org/sbekrin/styled-email-components)
[![dependencies Status](https://img.shields.io/david/sbekrin/styled-email-components.svg)](https://david-dm.org/sbekrin/styled-email-components)
[![devDependencies Status](https://img.shields.io/david/dev/sbekrin/styled-email-components.svg)](https://david-dm.org/sbekrin/styled-email-components?type=dev)

Extension of [`styled-components (v5.x)`](https://www.styled-components.com/) with
essential features for building email components.

## Features

- Styles are injected inline
- [Shorthand rules](./src/css-to-style.js#L6) are expanded
- [`styled.*` aliases](./src/utils/xhtml-elements.js) are XHTML compliant
- Supports [Outlook-specific elements](#outlook-specific-vml-elements)
- Compatible with [original APIs](https://www.styled-components.com/docs/api)
- Provides TypeScript typings

## Motivation

`styled-components` is a universal styling solution with great developer
experience and low learning curve. Unfortunately, there's no native support for
inline styling which is essential for building emails. This module adds all
necessary features to build mail-first components.

## Installation

yarn:

```sh
yarn add styled-email-components
```

npm:

```sh
npm install --save styled-email-components
```

## Getting Started

Check original
[Getting Started](https://www.styled-components.com/docs/basics#getting-started)
for more examples.

```js
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import styled from 'styled-email-components';

const Link = styled.a`
  font-family: sans-serif;
  background: blue;
  color: white;
`;

renderToStaticMarkup(<Link href="https://example.com">Hey</Link>),
// 👇 output
// <a href="https://example.com" style="font-family:sans-serif;background-color:blue;color:white;">Hey</a>
```

## API

### `styled.*`

This module sets list of XHTML 1.0 Transitional
[element aliases](./src/utils/xhtml-elements.js) instead of the original HTML5 set,
which is a widely used doctype in emails.

### Outlook-specific VML elements

In addition to XHTML elements, `styled.vml.*`, `styled.wml.*` and
`styled.office.*` aliases are available. These are simple proxies and pass tag
names as-is with `v:`, `w:` and `o:` prefixes respectively.

### Other APIs

[Original APIs](https://www.styled-components.com/docs/api) are mirrored without
any modifications from `styled-components`. Make sure to check
[server-side rendering](https://www.styled-components.com/docs/advanced#server-side-rendering)
guide for rendering the final email.

## License

MIT &copy; [Sergey Bekrin](http://bekrin.me/)

================================================
FILE: rollup.config.js
================================================
import nodeResolve from "rollup-plugin-node-resolve";
import replace from "rollup-plugin-replace";
import commonjs from "rollup-plugin-commonjs";
import babel from "rollup-plugin-babel";
import json from "rollup-plugin-json";
import flow from "rollup-plugin-flow";
import { terser } from "rollup-plugin-terser";
import sourceMaps from "rollup-plugin-sourcemaps";
import pkg from "./package.json";

/**
 * NODE_ENV explicit replacement is only needed for standalone packages, as webpack
 * automatically will replace it otherwise in the downstream build.
 */

const cjs = {
  exports: "named",
  format: "cjs",
  sourcemap: true,
};

const esm = {
  format: "esm",
  sourcemap: true,
};

const getCJS = (override) => ({ ...cjs, ...override });
const getESM = (override) => ({ ...esm, ...override });

const commonPlugins = [
  flow({
    // needed for sourcemaps to be properly generated
    pretty: true,
  }),
  sourceMaps(),
  json(),
  nodeResolve(),
  babel({
    configFile: require.resolve("./babel.config.js"),
    exclude: ["node_modules/**"],
  }),
  commonjs({
    ignoreGlobal: true,
    namedExports: {
      "react-is": ["isElement", "isValidElementType", "ForwardRef", "typeof"],
    },
  }),
  replace({
    __VERSION__: JSON.stringify(pkg.version),
  }),
];

// this should always be last
const minifierPlugin = terser({
  compress: {
    passes: 2,
  },
  sourcemap: true,
});

const configBase = {
  input: "./src/index.js",

  // \0 is rollup convention for generated in memory modules
  external: (id) =>
    !id.startsWith("\0") && !id.startsWith(".") && !id.startsWith("/"),
  plugins: commonPlugins,
};

const serverConfig = {
  ...configBase,
  output: [
    getESM({ file: "dist/styled-email-components.esm.js" }),
    getCJS({ file: "dist/styled-email-components.cjs.js" }),
  ],
  plugins: configBase.plugins.concat(
    replace({
      __SERVER__: JSON.stringify(true),
    }),
    minifierPlugin
  ),
};

const browserConfig = {
  ...configBase,
  output: [
    getESM({ file: "dist/styled-email-components.browser.esm.js" }),
    getCJS({ file: "dist/styled-email-components.browser.cjs.js" }),
  ],
  plugins: configBase.plugins.concat(
    replace({
      __SERVER__: JSON.stringify(false),
    }),
    minifierPlugin
  ),
};

export default [serverConfig, browserConfig];


================================================
FILE: src/css-to-style.js
================================================
import expandRule from "css-shorthand-expand";
import { conv as expandHexColor } from "color-shorthand-hex-to-six-digit";
import camelCase from "lodash.camelcase";

function shouldExpandRule(name) {
  return [
    "background",
    "font",
    "padding",
    "margin",
    "border",
    "border-width",
    "border-style",
    "border-color",
    "border-top",
    "border-right",
    "border-bottom",
    "border-left",
    "border-radius",
    "outline",
  ].includes(name);
}

function convertCssPairsToStyle(rules) {
  return (
    rules
      // Expand shorthand rules
      .reduce((out, [name, value]) => {
        if (shouldExpandRule(name)) {
          const expanded = expandRule(name, value);
          const next = Object.keys(expanded).map((rule) => [
            rule,
            expanded[rule],
          ]);
          return [...out, ...next];
        }
        return [...out, [name, value]];
      }, [])
      // Expand colors to 6 digits
      .reduce((out, [name, value]) => {
        return {
          ...out,
          [camelCase(name)]: /color/i.test(name)
            ? expandHexColor(value)
            : value,
        };
      }, {})
  );
}

export default convertCssPairsToStyle;


================================================
FILE: src/index.js
================================================
import constructWithOptions from "styled-components/src/constructors/constructWithOptions";
import xhtmlElements from "./xhtml-elements";
import StyleSheet from "./stylesheet";
import createInlineStyle from "./inline-style";
import createStyledEmailComponent from "./styled-email-component";

const InlineStyle = createInlineStyle(StyleSheet);
const StyledEmailComponent = createStyledEmailComponent(InlineStyle);
const styled = (tag) => constructWithOptions(StyledEmailComponent, tag);

// Set xhtml element aliases
xhtmlElements.forEach((element) =>
  Object.defineProperty(styled, element, {
    enumerable: true,
    configurable: false,
    get() {
      return styled(element);
    },
  })
);

// Set VML (v:*), WML (w:*) and Office (o:*) dynamic aliases
["vml", "wml", "office"].forEach((namespace) => {
  Object.defineProperty(styled, namespace, {
    enumerable: true,
    configurable: false,
    get() {
      const target = {};
      return new Proxy(target, {
        get(object, property) {
          if (property in object) {
            return object[property];
          }
          if (typeof property === "string") {
            return styled(`${namespace.charAt(0)}:${property}`);
          }
          return undefined;
        },
      });
    },
  });
});

export {
  createGlobalStyle,
  css,
  isStyledComponent,
  keyframes,
  ServerStyleSheet,
  StyleSheetConsumer,
  StyleSheetContext,
  StyleSheetManager,
  ThemeConsumer,
  ThemeContext,
  ThemeProvider,
  useTheme,
  version,
  withTheme,
} from "styled-components/src/base";

export default styled;


================================================
FILE: src/inline-style.js
================================================
import generateComponentId from "styled-components/src/utils/generateComponentId";
import flatten from "styled-components/src/utils/flatten";
import parse from "postcss-safe-parser";
import cssToStyle from "./css-to-style";

const generated = new Map();

export const resetStyleCache = () => {
  generated.clear();
};

export default (stylesheet) => {
  return class MailInlineStyle {
    constructor(rules) {
      this.rules = rules;
    }

    generateStyleObject(executionContext) {
      const flatStyles = flatten(this.rules, executionContext).join("");
      const hash = generateComponentId(flatStyles);
      if (!generated.has(hash)) {
        const rules = [];
        parse(flatStyles).each((node) => {
          switch (node.type) {
            case "decl":
              rules.push([node.prop, node.value]);
              return;
            case "comment":
              return;
            default:
              if (process.env.NODE_ENV !== "production") {
                console.warn(
                  `Node of type ${node.type} not supported as an inline style`
                );
              }
          }
        });
        const styles = cssToStyle(rules);
        const instance = stylesheet.create({ generated: styles });
        generated.set(hash, instance.generated);
      }
      return generated.get(hash);
    }
  };
};


================================================
FILE: src/is-vml-tag.js
================================================
export default function isVmlTag(tag) {
  return typeof tag === "string" && /^(v|w|o):/i.test(tag);
}


================================================
FILE: src/styled-email-component.js
================================================
import validAttr from "@emotion/is-prop-valid";
import hoist from "hoist-non-react-statics";
import React from "react";
import determineTheme from "styled-components/src/utils/determineTheme";
import { EMPTY_ARRAY, EMPTY_OBJECT } from "styled-components/src/utils/empties";
import generateComponentId from "styled-components/src/utils/generateComponentId";
import generateDisplayName from "styled-components/src/utils/generateDisplayName";
import getComponentName from "styled-components/src/utils/getComponentName";
import isTag from "styled-components/src/utils/isTag";
import isFunction from "styled-components/src/utils/isFunction";
import isStyledComponent from "styled-components/src/utils/isStyledComponent";
import merge from "styled-components/src/utils/mixinDeep";
import { ThemeContext } from "styled-components/src/models/ThemeProvider";
import StyleSheet from "./stylesheet";
import isVmlTag from "./is-vml-tag";

const identifiers = {};

/* We depend on components having unique IDs */
function generateId(displayName, parentComponentId) {
  const name = typeof displayName !== "string" ? "sc" : escape(displayName);
  // Ensure that no displayName can lead to duplicate componentIds
  identifiers[name] = (identifiers[name] || 0) + 1;
  const componentId = `${name}-${generateComponentId(
    name + identifiers[name]
  )}`;
  return parentComponentId
    ? `${parentComponentId}-${componentId}`
    : componentId;
}

function useResolvedAttrs(theme = EMPTY_OBJECT, props, attrs) {
  // NOTE: can't memoize this
  // returns [context, resolvedAttrs]
  // where resolvedAttrs is only the things injected by the attrs themselves
  const context = { ...props, theme };
  const resolvedAttrs = {};

  attrs.forEach((attrDef) => {
    let resolvedAttrDef = attrDef;
    let key;

    if (isFunction(resolvedAttrDef)) {
      resolvedAttrDef = resolvedAttrDef(context);
    }

    /* eslint-disable guard-for-in */
    for (key in resolvedAttrDef) {
      context[key] = resolvedAttrs[key] = resolvedAttrDef[key];
    }
    /* eslint-enable guard-for-in */
  });

  return [context, resolvedAttrs];
}

function useStyledComponentImpl(forwardedComponent, props, forwardedRef) {
  const {
    attrs: componentAttrs,
    inlineStyle,
    defaultProps,
    shouldForwardProp,
    target,
  } = forwardedComponent;

  // NOTE: the non-hooks version only subscribes to this when !componentStyle.isStatic,
  // but that'd be against the rules-of-hooks. We could be naughty and do it anyway as it
  // should be an immutable value, but behave for now.
  const theme = determineTheme(
    props,
    React.useContext(ThemeContext),
    defaultProps
  );

  const [context, attrs] = useResolvedAttrs(
    theme || EMPTY_OBJECT,
    props,
    componentAttrs
  );

  const generatedStyles = inlineStyle.generateStyleObject(context);

  const refToForward = forwardedRef;

  const elementToBeCreated =
    attrs.$as || props.$as || attrs.as || props.as || target;

  const isTargetTag = isTag(elementToBeCreated);
  const isTargetVmlTag = isVmlTag(elementToBeCreated);
  const computedProps = attrs !== props ? { ...props, ...attrs } : props;
  const propsForElement = {};

  // eslint-disable-next-line guard-for-in
  for (const key in computedProps) {
    if (key[0] === "$" || key === "as") continue;
    else if (key === "forwardedAs") {
      propsForElement.as = computedProps[key];
    } else if (
      shouldForwardProp
        ? shouldForwardProp(key, validAttr, elementToBeCreated)
        : isTargetVmlTag
        ? true
        : isTargetTag
        ? validAttr(key)
        : true
    ) {
      propsForElement[key] = computedProps[key];
    }
  }

  propsForElement.style = StyleSheet.flatten([
    generatedStyles,
    props.style,
    attrs.style,
  ]);

  propsForElement.ref = refToForward;

  return React.createElement(elementToBeCreated, propsForElement);
}

export default (MailInlineStyle) => {
  const createStyledEmailComponent = (target, options, rules) => {
    const isTargetStyledComp = isStyledComponent(target);

    const {
      displayName = generateDisplayName(target),
      componentId = generateId(options.displayName, options.parentComponentId),
      attrs = EMPTY_ARRAY,
    } = options;

    const styledComponentId =
      options.displayName && options.componentId
        ? `${escape(options.displayName)}-${options.componentId}`
        : options.componentId || componentId;

    // fold the underlying StyledComponent attrs up (implicit extend)
    const finalAttrs =
      isTargetStyledComp && target.attrs
        ? target.attrs.concat(attrs).filter(Boolean)
        : attrs;

    // eslint-disable-next-line prefer-destructuring
    let shouldForwardProp = options.shouldForwardProp;

    if (isTargetStyledComp && target.shouldForwardProp) {
      if (options.shouldForwardProp) {
        // compose nested shouldForwardProp calls
        shouldForwardProp = (prop, filterFn, elementToBeCreated) =>
          target.shouldForwardProp(prop, filterFn, elementToBeCreated) &&
          options.shouldForwardProp(prop, filterFn, elementToBeCreated);
      } else {
        // eslint-disable-next-line prefer-destructuring
        shouldForwardProp = target.shouldForwardProp;
      }
    }

    /**
     * forwardRef creates a new interim component, which we'll take advantage of
     * instead of extending ParentComponent to create _another_ interim class
     */
    let WrappedStyledComponent;

    // eslint-disable-next-line react-hooks/rules-of-hooks
    const forwardRef = (props, ref) =>
      useStyledComponentImpl(WrappedStyledComponent, props, ref);

    forwardRef.displayName = displayName;

    WrappedStyledComponent = React.forwardRef(forwardRef);

    WrappedStyledComponent.attrs = finalAttrs;
    WrappedStyledComponent.inlineStyle = new MailInlineStyle(
      isTargetStyledComp ? target.inlineStyle.rules.concat(rules) : rules
    );
    WrappedStyledComponent.displayName = displayName;
    WrappedStyledComponent.shouldForwardProp = shouldForwardProp;

    WrappedStyledComponent.styledComponentId = styledComponentId;

    // fold the underlying StyledComponent target up since we folded the styles
    WrappedStyledComponent.target = isTargetStyledComp ? target.target : target;

    WrappedStyledComponent.withComponent = function withComponent(tag) {
      const { componentId: previousComponentId, ...optionsToCopy } = options;

      const newComponentId =
        previousComponentId &&
        `${previousComponentId}-${escape(getComponentName(tag))}`;

      const newOptions = {
        ...optionsToCopy,
        attrs: finalAttrs,
        componentId: newComponentId,
      };

      return createStyledEmailComponent(tag, newOptions, rules);
    };

    Object.defineProperty(WrappedStyledComponent, "defaultProps", {
      get() {
        return this._foldedDefaultProps;
      },

      set(obj) {
        this._foldedDefaultProps = isTargetStyledComp
          ? merge({}, target.defaultProps, obj)
          : obj;
      },
    });

    hoist(WrappedStyledComponent, target, {
      // all SC-specific things should not be hoisted
      attrs: true,
      inlineStyle: true,
      displayName: true,
      shouldForwardProp: true,
      styledComponentId: true,
      target: true,
      withComponent: true,
    });

    return WrappedStyledComponent;
  };

  return createStyledEmailComponent;
};


================================================
FILE: src/stylesheet.js
================================================
let lastId = 0;
const registry = {};

const guid = () => lastId++;

const registerStyle = (style) => {
  const id = guid();
  registry[id] = style;
  return id;
};

const resolveStyle = (id) => registry[id];

const create = (styles) => {
  const result = {};
  Object.keys(styles).forEach((key) => {
    result[key] = registerStyle(styles[key]);
  });
  return result;
};

const merge = (left = {}, right = {}) => ({ ...left, ...right });

const flatten = (input) => {
  if (Array.isArray(input)) {
    return input.reduce((acc, val) => merge(acc, flatten(val)), {});
  } else if (typeof input === "number") {
    return resolveStyle(input);
  } else if (!input) {
    return undefined;
  }
  return input;
};

const resolve = (style) => flatten(style);

const StyleSheet = {
  hairlineWidth: 1,
  absoluteFill: registerStyle({
    position: "absolute",
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  }),
  create,
  flatten,
  resolve,
};

export default StyleSheet;


================================================
FILE: src/xhtml-elements.js
================================================
// List of XHTML 1.0 Transitional elements
export default [
  "a",
  "abbr",
  "acronym",
  "address",
  "applet",
  "area",
  "b",
  "base",
  "basefont",
  "bdo",
  "big",
  "blockquote",
  "body",
  "br",
  "button",
  "caption",
  "center",
  "cite",
  "code",
  "col",
  "colgroup",
  "dd",
  "del",
  "dfn",
  "dir",
  "div",
  "dl",
  "dt",
  "em",
  "fieldset",
  "font",
  "form",
  "h1",
  "h2",
  "h3",
  "h4",
  "h5",
  "h6",
  "head",
  "hr",
  "html",
  "i",
  "iframe",
  "img",
  "input",
  "ins",
  "isindex",
  "kbd",
  "label",
  "legend",
  "li",
  "link",
  "map",
  "menu",
  "meta",
  "noframes",
  "noscript",
  "object",
  "ol",
  "optgroup",
  "option",
  "p",
  "param",
  "pre",
  "q",
  "s",
  "samp",
  "script",
  "select",
  "small",
  "span",
  "strike",
  "strong",
  "style",
  "sub",
  "sup",
  "table",
  "tbody",
  "td",
  "textarea",
  "tfoot",
  "th",
  "thead",
  "title",
  "tr",
  "tt",
  "u",
  "ul",
  "var",
];


================================================
FILE: styled-email-components.d.ts
================================================
import * as React from "react";
import {
  css,
  keyframes,
  injectGlobal,
  isStyledComponent,
  consolidateStreamedStyles,
  ThemeProvider,
  withTheme,
  ServerStyleSheet,
  StyleSheetManager,
  ThemedStyledFunction,
  StyledComponentClass,
} from "styled-components";

// #region xhtmltypes
export interface XHTMLElements {
  a: React.DetailedHTMLProps<
    React.AnchorHTMLAttributes<HTMLAnchorElement>,
    HTMLAnchorElement
  >;
  abbr: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  acronym: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  address: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  applet: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  area: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLAreaElement>,
    HTMLAreaElement
  >;
  b: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  base: React.DetailedHTMLProps<
    React.BaseHTMLAttributes<HTMLBaseElement>,
    HTMLBaseElement
  >;
  basefont: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  bdo: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  big: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  blockquote: React.DetailedHTMLProps<
    React.BlockquoteHTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  body: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLBodyElement>,
    HTMLBodyElement
  >;
  br: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLBRElement>,
    HTMLBRElement
  >;
  button: React.DetailedHTMLProps<
    React.ButtonHTMLAttributes<HTMLButtonElement>,
    HTMLButtonElement
  >;
  caption: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  center: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  cite: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  code: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  col: React.DetailedHTMLProps<
    React.ColHTMLAttributes<HTMLTableColElement>,
    HTMLTableColElement
  >;
  colgroup: React.DetailedHTMLProps<
    React.ColgroupHTMLAttributes<HTMLTableColElement>,
    HTMLTableColElement
  >;
  dd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  del: React.DetailedHTMLProps<
    React.DelHTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  dfn: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  dir: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  div: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLDivElement>,
    HTMLDivElement
  >;
  dl: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLDListElement>,
    HTMLDListElement
  >;
  dt: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  em: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  fieldset: React.DetailedHTMLProps<
    React.FieldsetHTMLAttributes<HTMLFieldSetElement>,
    HTMLFieldSetElement
  >;
  font: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLFontElement>,
    HTMLFontElement
  >;
  form: React.DetailedHTMLProps<
    React.FormHTMLAttributes<HTMLFormElement>,
    HTMLFormElement
  >;
  h1: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  >;
  h2: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  >;
  h3: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  >;
  h4: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  >;
  h5: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  >;
  h6: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  >;
  head: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHeadElement>,
    HTMLHeadElement
  >;
  hr: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLHRElement>,
    HTMLHRElement
  >;
  html: React.DetailedHTMLProps<
    React.HtmlHTMLAttributes<HTMLHtmlElement>,
    HTMLHtmlElement
  >;
  i: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  iframe: React.DetailedHTMLProps<
    React.IframeHTMLAttributes<HTMLIFrameElement>,
    HTMLIFrameElement
  >;
  img: React.DetailedHTMLProps<
    React.ImgHTMLAttributes<HTMLImageElement>,
    HTMLImageElement
  >;
  input: React.DetailedHTMLProps<
    React.InputHTMLAttributes<HTMLInputElement>,
    HTMLInputElement
  >;
  ins: React.DetailedHTMLProps<
    React.InsHTMLAttributes<HTMLModElement>,
    HTMLModElement
  >;
  isindex: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  label: React.DetailedHTMLProps<
    React.LabelHTMLAttributes<HTMLLabelElement>,
    HTMLLabelElement
  >;
  legend: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLLegendElement>,
    HTMLLegendElement
  >;
  li: React.DetailedHTMLProps<
    React.LiHTMLAttributes<HTMLLIElement>,
    HTMLLIElement
  >;
  link: React.DetailedHTMLProps<
    React.LinkHTMLAttributes<HTMLLinkElement>,
    HTMLLinkElement
  >;
  map: React.DetailedHTMLProps<
    React.MapHTMLAttributes<HTMLMapElement>,
    HTMLMapElement
  >;
  menu: React.DetailedHTMLProps<
    React.MenuHTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  meta: React.DetailedHTMLProps<
    React.MetaHTMLAttributes<HTMLMetaElement>,
    HTMLMetaElement
  >;
  noframes: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  noscript: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  object: React.DetailedHTMLProps<
    React.ObjectHTMLAttributes<HTMLObjectElement>,
    HTMLObjectElement
  >;
  ol: React.DetailedHTMLProps<
    React.OlHTMLAttributes<HTMLOListElement>,
    HTMLOListElement
  >;
  optgroup: React.DetailedHTMLProps<
    React.OptgroupHTMLAttributes<HTMLOptGroupElement>,
    HTMLOptGroupElement
  >;
  option: React.DetailedHTMLProps<
    React.OptionHTMLAttributes<HTMLOptionElement>,
    HTMLOptionElement
  >;
  p: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLParagraphElement>,
    HTMLParagraphElement
  >;
  param: React.DetailedHTMLProps<
    React.ParamHTMLAttributes<HTMLParamElement>,
    HTMLParamElement
  >;
  pre: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLPreElement>,
    HTMLPreElement
  >;
  q: React.DetailedHTMLProps<
    React.QuoteHTMLAttributes<HTMLQuoteElement>,
    HTMLQuoteElement
  >;
  s: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  samp: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  script: React.DetailedHTMLProps<
    React.ScriptHTMLAttributes<HTMLScriptElement>,
    HTMLScriptElement
  >;
  select: React.DetailedHTMLProps<
    React.SelectHTMLAttributes<HTMLSelectElement>,
    HTMLSelectElement
  >;
  small: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  span: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLSpanElement>,
    HTMLSpanElement
  >;
  strike: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  strong: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;
  style: React.DetailedHTMLProps<
    React.StyleHTMLAttributes<HTMLStyleElement>,
    HTMLStyleElement
  >;
  sub: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  sup: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  table: React.DetailedHTMLProps<
    React.TableHTMLAttributes<HTMLTableElement>,
    HTMLTableElement
  >;
  tbody: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLTableSectionElement>,
    HTMLTableSectionElement
  >;
  td: React.DetailedHTMLProps<
    React.TdHTMLAttributes<HTMLTableDataCellElement>,
    HTMLTableDataCellElement
  >;
  textarea: React.DetailedHTMLProps<
    React.TextareaHTMLAttributes<HTMLTextAreaElement>,
    HTMLTextAreaElement
  >;
  tfoot: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLTableSectionElement>,
    HTMLTableSectionElement
  >;
  th: React.DetailedHTMLProps<
    React.ThHTMLAttributes<HTMLTableHeaderCellElement>,
    HTMLTableHeaderCellElement
  >;
  thead: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLTableSectionElement>,
    HTMLTableSectionElement
  >;
  title: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLTitleElement>,
    HTMLTitleElement
  >;
  tr: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLTableRowElement>,
    HTMLTableRowElement
  >;
  tt: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  u: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
  ul: React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLUListElement>,
    HTMLUListElement
  >;
  var: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
}
// #endregion

// #region vmlelements
export type VMLAttrs = { [attr: string]: any };
export interface VMLElements {
  shape: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  shapetype: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  group: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  background: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  path: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  formulas: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  handles: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  fill: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  stroke: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  shadow: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  textbox: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  textpath: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  imagedata: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  line: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  polyline: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  curve: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  roundrect: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  oval: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  arc: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
  image: React.DetailedHTMLProps<VMLAttrs, HTMLElement>;
}
// #endregion

// Helpers, copied from styled-components.d.ts
type KeyofBase = keyof any;
type Diff<T extends KeyofBase, U extends KeyofBase> = ({ [P in T]: P } &
  { [P in U]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
type WithOptionalTheme<P extends { theme?: T }, T> = Omit<P, "theme"> & {
  theme?: T;
};

// Re-define all types with XHTML elements
type ThemedStyledComponentFactories<T> = {
  [TTag in keyof XHTMLElements]: ThemedStyledFunction<XHTMLElements[TTag], T>;
};
export interface ThemedBaseStyledInterface<T>
  extends ThemedStyledComponentFactories<T> {
  <P, TTag extends keyof XHTMLElements>(tag: TTag): ThemedStyledFunction<
    P,
    T,
    P & XHTMLElements[TTag]
  >;
  <P, O>(component: StyledComponentClass<P, T, O>): ThemedStyledFunction<
    P,
    T,
    O
  >;
  <P extends { [prop: string]: any; theme?: T }>(
    component: React.ComponentType<P>
  ): ThemedStyledFunction<P, T, WithOptionalTheme<P, T>>;
}
export type BaseStyledInterface = ThemedBaseStyledInterface<any>;
export type ThemedStyledInterface<T> = ThemedBaseStyledInterface<T>;

// Define vml.*, wml.* and office.* proxy
type AnyAttrs<T> = { [attr: string]: T };
type NamespacedElementFactory<T> = {
  [tag: string]: ThemedStyledFunction<
    React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & AnyAttrs<any>,
      HTMLElement
    >,
    T
  >;
};
type VMLNamespaceFactory<T> = {
  [TTag in keyof VMLElements]: ThemedStyledFunction<VMLElements[TTag], T>;
};
type WMLNamespaceFactory<T> = {
  [tag: string]: ThemedStyledFunction<any, T>;
};
type OfficeNamespaceFactory<T> = {
  [tag: string]: ThemedStyledFunction<any, T>;
};
interface NamespacedStyledInterface<T> extends ThemedStyledInterface<T> {
  vml: VMLNamespaceFactory<any>;
  wml: WMLNamespaceFactory<any>;
  office: OfficeNamespaceFactory<any>;
}

// Re-export it
export type StyledInterface = NamespacedStyledInterface<any>;
declare const styled: StyledInterface;

export * from "styled-components";

export default styled;


================================================
FILE: test/namespace.spec.js
================================================
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import prettier from "prettier";
import styled from "../src";

const format = (html) => prettier.format(html, { parser: "html" });

describe("namespace", () => {
  it("supports vml, wml, and office namespaces", () => {
    const RoundRect = styled.vml.roundrect.attrs({
      arcsize: "10%",
      strokecolor: "#1e3650",
      fill: "true",
    })`
      v-text-anchor: middle;
      height: 40px;
      width: 200px;
    `;
    const Lock = styled.wml.anchorlock``;
    const Fill = styled.vml.fill.attrs({
      type: "tile",
      src: "https://i.imgur.com/0xPEf.gif",
      color: "#556270",
    })``;
    const Text = styled.center`
      font-family: sans-serif;
      font-weight: bold;
      font-size: 13px;
      color: #fff;
    `;
    const Button = ({ children, ...rest }) => {
      children;
      return (
        <RoundRect {...rest}>
          <Lock />
          <Fill />
          <Text>{children}</Text>
        </RoundRect>
      );
    };
    expect(
      format(
        renderToStaticMarkup(
          <Button href="https://example.com">Click me</Button>
        )
      )
    ).toMatchInlineSnapshot(`
      "<v:roundrect
        href=\\"https://example.com\\"
        arcsize=\\"10%\\"
        strokecolor=\\"#1e3650\\"
        fill=\\"true\\"
        style=\\"v-text-anchor: middle; height: 40px; width: 200px\\"
        ><w:anchorlock></w:anchorlock
        ><v:fill
          type=\\"tile\\"
          src=\\"https://i.imgur.com/0xPEf.gif\\"
          color=\\"#556270\\"
        ></v:fill>
        <center
          style=\\"
            font-family: sans-serif;
            font-weight: bold;
            font-size: 13px;
            color: #ffffff;
          \\"
        >
          Click me
        </center></v:roundrect
      >
      "
    `);
  });
});


================================================
FILE: test/specific.spec.js
================================================
import React from "react";
import ReactTestRenderer from "react-test-renderer";
import styled from "../src";

const render = (element) => ReactTestRenderer.create(element).toJSON();

describe("specific", () => {
  it("supports mail-specific props", () => {
    const MsoComponent = styled.center`
      mso-hide: all;
    `;
    expect(render(<MsoComponent />)).toMatchInlineSnapshot(`
<center
  style={
    Object {
      "msoHide": "all",
    }
  }
/>
`);
  });

  it("supports vml specific elements", () => {
    const AnchorLock = styled("w:anchorlock")``;
    expect(render(<AnchorLock />)).toMatchInlineSnapshot(`
<w:anchorlock
  style={Object {}}
/>
`);
  });

  it("supports custom attributes", () => {
    const RoundRect = styled.vml.roundrect.attrs({
      "xmlns:v": "urn:schemas-microsoft-com:vml",
      "xmlns:w": "urn:schemas-microsoft-com:office:word",
    })``;
    expect(render(<RoundRect />)).toMatchInlineSnapshot(`
<v:roundrect
  style={Object {}}
  xmlns:v="urn:schemas-microsoft-com:vml"
  xmlns:w="urn:schemas-microsoft-com:office:word"
/>
`);
  });

  it("outputs correct units", () => {
    const Box = styled.center`
      padding: 10px;
      margin: 10in;
      left: -1em;
      right: 5pt;
      line-height: 1.2;
    `;
    expect(render(<Box />)).toMatchInlineSnapshot(`
<center
  style={
    Object {
      "left": "-1em",
      "lineHeight": "1.2",
      "marginBottom": "10in",
      "marginLeft": "10in",
      "marginRight": "10in",
      "marginTop": "10in",
      "paddingBottom": "10px",
      "paddingLeft": "10px",
      "paddingRight": "10px",
      "paddingTop": "10px",
      "right": "5pt",
    }
  }
/>
`);
  });

  it("expands color to 6 digits", () => {
    const Color = styled.p`
      color: #000;
      border: 1px solid #333;
    `;
    expect(render(<Color />)).toMatchInlineSnapshot(`
<p
  style={
    Object {
      "borderBottomColor": "#333333",
      "borderBottomStyle": "solid",
      "borderBottomWidth": "1px",
      "borderLeftColor": "#333333",
      "borderLeftStyle": "solid",
      "borderLeftWidth": "1px",
      "borderRightColor": "#333333",
      "borderRightStyle": "solid",
      "borderRightWidth": "1px",
      "borderTopColor": "#333333",
      "borderTopStyle": "solid",
      "borderTopWidth": "1px",
      "color": "#000000",
    }
  }
/>
`);
  });
});


================================================
FILE: test/styled.spec.js
================================================
import React from "react";
import ReactTestRenderer from "react-test-renderer";
import styled, { css } from "../src";

const render = (element) => ReactTestRenderer.create(element).toJSON();

describe("styled", () => {
  it("works with `styled.component`", () => {
    const Link = styled.a`
      font-weight: bold;
      color: blue;
    `;
    expect(render(<Link>Hey</Link>)).toMatchInlineSnapshot(`
<a
  style={
    Object {
      "color": "blue",
      "fontWeight": "bold",
    }
  }
>
  Hey
</a>
`);
  });

  it("works with `styled(Component)`", () => {
    const Link = styled((props) => <a {...props} />)`
      font-weight: bold;
      color: green;
    `;
    expect(render(<Link>Hey</Link>)).toMatchInlineSnapshot(`
<a
  style={
    Object {
      "color": "green",
      "fontWeight": "bold",
    }
  }
>
  Hey
</a>
`);
  });

  it("interpolates props-based values correctly", () => {
    const Text = styled.p`
      color: #333;
      font-size: ${(props) => (props.big ? 32 : 16)}px;
    `;
    expect(render(<Text big>Hey</Text>)).toMatchInlineSnapshot(`
<p
  style={
    Object {
      "color": "#333333",
      "fontSize": "32px",
    }
  }
>
  Hey
</p>
`);
  });

  it("composes components", () => {
    const Link = styled.a`
      text-decoration: underline;
      font-size: 16px;
      color: blue;
    `;
    const Button = styled(Link)`
      text-decoration: none;
      font-weight: bold;
      padding: 5px 10px;
      background: blue;
      color: white;
    `;
    const BigButton = styled(Button)`
      padding: 10px 20px;
      font-size: 20px;
    `;
    expect(render(<BigButton href="https://example.com">Click me</BigButton>))
      .toMatchInlineSnapshot(`
<a
  href="https://example.com"
  style={
    Object {
      "backgroundColor": "blue",
      "color": "white",
      "fontSize": "20px",
      "fontWeight": "bold",
      "paddingBottom": "10px",
      "paddingLeft": "20px",
      "paddingRight": "20px",
      "paddingTop": "10px",
      "textDecoration": "none",
    }
  }
>
  Click me
</a>
`);
  });

  it("works with `.attrs`", () => {
    const Action = styled.a.attrs({
      href: "https://example.com",
      target: "blank",
    })`
      color: blue;
    `;
    expect(render(<Action>Click me</Action>)).toMatchInlineSnapshot(`
<a
  href="https://example.com"
  style={
    Object {
      "color": "blue",
    }
  }
  target="blank"
>
  Click me
</a>
`);
  });

  it("works with `.withComponent`", () => {
    const Text = styled.p`
      color: green;
    `;
    const Heading = Text.withComponent("h1");
    expect(render(<Heading>Hey</Heading>)).toMatchInlineSnapshot(`
<h1
  style={
    Object {
      "color": "green",
    }
  }
>
  Hey
</h1>
`);
  });

  it("works with `css` helper", () => {
    const mixin = css`
      color: ${(props) => (props.primary ? "blue" : "#333")};
    `;
    const Link = styled.a`
      font-size: 16px;
      ${mixin};
    `;
    expect(render(<Link primary>Click</Link>)).toMatchInlineSnapshot(`
<a
  style={
    Object {
      "color": "blue",
      "fontSize": "16px",
    }
  }
>
  Click
</a>
`);
  });

  it("warns about nested rules", () => {
    let lastConsoleWarn;
    const originalConsoleWarn = console.warn;
    console.warn = (...message) => (lastConsoleWarn = message.join(" "));
    const Invalid = styled.div`
      color: #333;

      // This won't work
      .nested {
        color: blue;
      }
    `;
    expect(render(<Invalid />)).toMatchInlineSnapshot(`
<div
  style={
    Object {
      "color": "#333333",
    }
  }
/>
`);
    expect(lastConsoleWarn).toMatchInlineSnapshot(
      `"Node of type rule not supported as an inline style"`
    );
    console.warn = originalConsoleWarn;
  });

  it("expands style rules", () => {
    const Box = styled.p`
      border: 1px solid #333;
      background: #333;
    `;
    expect(render(<Box />)).toMatchInlineSnapshot(`
<p
  style={
    Object {
      "backgroundColor": "#333333",
      "borderBottomColor": "#333333",
      "borderBottomStyle": "solid",
      "borderBottomWidth": "1px",
      "borderLeftColor": "#333333",
      "borderLeftStyle": "solid",
      "borderLeftWidth": "1px",
      "borderRightColor": "#333333",
      "borderRightStyle": "solid",
      "borderRightWidth": "1px",
      "borderTopColor": "#333333",
      "borderTopStyle": "solid",
      "borderTopWidth": "1px",
    }
  }
/>
`);
  });

  it("overrides and merges styles by value from `style` prop", () => {
    const Link = styled.a`
      font-size: 16px;
      color: red;
    `;
    expect(render(<Link style={{ color: "blue" }} />)).toMatchInlineSnapshot(`
<a
  style={
    Object {
      "color": "blue",
      "fontSize": "16px",
    }
  }
/>
`);
  });

  it("overrides and merges styles by value from `.attrs`", () => {
    const Link = styled.a.attrs({
      style: {
        color: "red",
      },
    })`
      font-size: 16px;
      color: blue;
    `;
    expect(render(<Link />)).toMatchInlineSnapshot(`
<a
  style={
    Object {
      "color": "red",
      "fontSize": "16px",
    }
  }
/>
`);
  });

  it("works with styles-returning function API", () => {
    const Link = styled.a(({ color }) => ({ color }));
    expect(render(<Link color="blue" />)).toMatchInlineSnapshot(`
<a
  color="blue"
  style={
    Object {
      "color": "blue",
    }
  }
/>
`);
  });
});


================================================
FILE: test/theme.spec.js
================================================
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import styled, { withTheme, ThemeProvider } from "../src";

describe("theme", () => {
  it("works with `styled.element`", () => {
    const Link = styled.a`
      color: ${(props) => props.theme.color};
    `;
    expect(
      renderToStaticMarkup(
        <ThemeProvider theme={{ color: "green" }}>
          <Link>Link</Link>
        </ThemeProvider>
      )
    ).toMatchInlineSnapshot(`"<a style=\\"color:green\\">Link</a>"`);
  });

  it("works with `styled(Component)`", () => {
    const Heading = (props) => <h1 {...props} />;
    const StyledHeading = styled(Heading)`
      font-size: ${(props) => props.theme.size};
    `;
    expect(
      renderToStaticMarkup(
        <ThemeProvider theme={{ size: "30px" }}>
          <StyledHeading>Hey</StyledHeading>
        </ThemeProvider>
      )
    ).toMatchInlineSnapshot(`"<h1 style=\\"font-size:30px\\">Hey</h1>"`);
  });

  it("works with `withTheme`", () => {
    const Component = ({ theme, innerRef, ...rest }) => <div {...rest} />;
    const ThemedComponent = withTheme(Component);
    const WrapperComponent = (props) => <ThemedComponent {...props} />;
    const StyledComponent = styled(WrapperComponent)``;
    expect(
      renderToStaticMarkup(
        <ThemeProvider theme={{ attribute: 42 }}>
          <StyledComponent />
        </ThemeProvider>
      )
    ).toMatchInlineSnapshot(`"<div></div>"`);
  });
});


================================================
FILE: test/typings.spec.tsx
================================================
// @ts-check
import styled from "../";

describe("typings", () => {
  it("does not report ts error", () => {
    const StyledCenter = styled.center``;

    const StyledFont = styled.font``;

    const StyledVmlRoundRect = styled.vml.roundrect``;

    const StyledAnchorLock = styled.wml.anchorlock``;

    const StyledVmlRoundRectAttrs = styled.vml.roundrect.attrs({
      "xmlns:v": "urn:schemas-microsoft-com:vml",
      "xmlns:w": "urn:schemas-microsoft-com:office:word",
    })``;
  });
});


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}
Download .txt
gitextract_4t0ert70/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .gitmodules
├── .vscode/
│   └── settings.json
├── LICENSE
├── babel.config.js
├── jest.config.js
├── package.json
├── readme.md
├── rollup.config.js
├── src/
│   ├── css-to-style.js
│   ├── index.js
│   ├── inline-style.js
│   ├── is-vml-tag.js
│   ├── styled-email-component.js
│   ├── stylesheet.js
│   └── xhtml-elements.js
├── styled-email-components.d.ts
├── test/
│   ├── namespace.spec.js
│   ├── specific.spec.js
│   ├── styled.spec.js
│   ├── theme.spec.js
│   └── typings.spec.tsx
└── tsconfig.json
Download .txt
SYMBOL INDEX (30 symbols across 6 files)

FILE: src/css-to-style.js
  function shouldExpandRule (line 5) | function shouldExpandRule(name) {
  function convertCssPairsToStyle (line 24) | function convertCssPairsToStyle(rules) {

FILE: src/index.js
  method get (line 16) | get() {
  method get (line 27) | get() {

FILE: src/inline-style.js
  method constructor (line 14) | constructor(rules) {
  method generateStyleObject (line 18) | generateStyleObject(executionContext) {

FILE: src/is-vml-tag.js
  function isVmlTag (line 1) | function isVmlTag(tag) {

FILE: src/styled-email-component.js
  function generateId (line 20) | function generateId(displayName, parentComponentId) {
  function useResolvedAttrs (line 32) | function useResolvedAttrs(theme = EMPTY_OBJECT, props, attrs) {
  function useStyledComponentImpl (line 57) | function useStyledComponentImpl(forwardedComponent, props, forwardedRef) {
  method get (line 201) | get() {
  method set (line 205) | set(obj) {

FILE: styled-email-components.d.ts
  type XHTMLElements (line 17) | interface XHTMLElements {
  type VMLAttrs (line 318) | type VMLAttrs = { [attr: string]: any };
  type VMLElements (line 319) | interface VMLElements {
  type KeyofBase (line 344) | type KeyofBase = keyof any;
  type Diff (line 345) | type Diff<T extends KeyofBase, U extends KeyofBase> = ({ [P in T]: P } &
  type Omit (line 347) | type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
  type WithOptionalTheme (line 348) | type WithOptionalTheme<P extends { theme?: T }, T> = Omit<P, "theme"> & {
  type ThemedStyledComponentFactories (line 353) | type ThemedStyledComponentFactories<T> = {
  type ThemedBaseStyledInterface (line 356) | interface ThemedBaseStyledInterface<T>
  type BaseStyledInterface (line 372) | type BaseStyledInterface = ThemedBaseStyledInterface<any>;
  type ThemedStyledInterface (line 373) | type ThemedStyledInterface<T> = ThemedBaseStyledInterface<T>;
  type AnyAttrs (line 376) | type AnyAttrs<T> = { [attr: string]: T };
  type NamespacedElementFactory (line 377) | type NamespacedElementFactory<T> = {
  type VMLNamespaceFactory (line 386) | type VMLNamespaceFactory<T> = {
  type WMLNamespaceFactory (line 389) | type WMLNamespaceFactory<T> = {
  type OfficeNamespaceFactory (line 392) | type OfficeNamespaceFactory<T> = {
  type NamespacedStyledInterface (line 395) | interface NamespacedStyledInterface<T> extends ThemedStyledInterface<T> {
  type StyledInterface (line 402) | type StyledInterface = NamespacedStyledInterface<any>;
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (52K chars).
[
  {
    "path": ".github/workflows/test.yml",
    "chars": 370,
    "preview": "name: Test\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  build:\n    runs-o"
  },
  {
    "path": ".gitignore",
    "chars": 25,
    "preview": "node_modules/\ndist/\n*.log"
  },
  {
    "path": ".gitmodules",
    "chars": 151,
    "preview": "[submodule \"styled-components\"]\n\tpath = vendor/styled-components\n\turl = https://github.com/styled-components/styled-comp"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 43,
    "preview": "{\n  \"cSpell.words\": [\n    \"camelcase\"\n  ]\n}"
  },
  {
    "path": "LICENSE",
    "chars": 1070,
    "preview": "MIT License\n\nCopyright (c) 2021 Sergey Bekrin\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
  },
  {
    "path": "babel.config.js",
    "chars": 436,
    "preview": "module.exports = {\n  presets: [\"./vendor/styled-components/babel-preset\"],\n  plugins: [\n    // Replace `styled-component"
  },
  {
    "path": "jest.config.js",
    "chars": 247,
    "preview": "module.exports = {\n  testPathIgnorePatterns: [\"/node_modules/\", \"/vendor/\"],\n  transformIgnorePatterns: [\"/node_modules/"
  },
  {
    "path": "package.json",
    "chars": 2437,
    "preview": "{\n  \"name\": \"styled-email-components\",\n  \"version\": \"5.0.3\",\n  \"description\": \"💌 styled-components for emails\",\n  \"repos"
  },
  {
    "path": "readme.md",
    "chars": 2863,
    "preview": "# 💌 styled-email-components\n\n[![npm Version](https://img.shields.io/npm/v/styled-email-components.svg)](https://www.npmj"
  },
  {
    "path": "rollup.config.js",
    "chars": 2305,
    "preview": "import nodeResolve from \"rollup-plugin-node-resolve\";\nimport replace from \"rollup-plugin-replace\";\nimport commonjs from "
  },
  {
    "path": "src/css-to-style.js",
    "chars": 1211,
    "preview": "import expandRule from \"css-shorthand-expand\";\nimport { conv as expandHexColor } from \"color-shorthand-hex-to-six-digit\""
  },
  {
    "path": "src/index.js",
    "chars": 1582,
    "preview": "import constructWithOptions from \"styled-components/src/constructors/constructWithOptions\";\nimport xhtmlElements from \"."
  },
  {
    "path": "src/inline-style.js",
    "chars": 1356,
    "preview": "import generateComponentId from \"styled-components/src/utils/generateComponentId\";\nimport flatten from \"styled-component"
  },
  {
    "path": "src/is-vml-tag.js",
    "chars": 102,
    "preview": "export default function isVmlTag(tag) {\n  return typeof tag === \"string\" && /^(v|w|o):/i.test(tag);\n}\n"
  },
  {
    "path": "src/styled-email-component.js",
    "chars": 7386,
    "preview": "import validAttr from \"@emotion/is-prop-valid\";\nimport hoist from \"hoist-non-react-statics\";\nimport React from \"react\";\n"
  },
  {
    "path": "src/stylesheet.js",
    "chars": 977,
    "preview": "let lastId = 0;\nconst registry = {};\n\nconst guid = () => lastId++;\n\nconst registerStyle = (style) => {\n  const id = guid"
  },
  {
    "path": "src/xhtml-elements.js",
    "chars": 957,
    "preview": "// List of XHTML 1.0 Transitional elements\nexport default [\n  \"a\",\n  \"abbr\",\n  \"acronym\",\n  \"address\",\n  \"applet\",\n  \"ar"
  },
  {
    "path": "styled-email-components.d.ts",
    "chars": 12494,
    "preview": "import * as React from \"react\";\nimport {\n  css,\n  keyframes,\n  injectGlobal,\n  isStyledComponent,\n  consolidateStreamedS"
  },
  {
    "path": "test/namespace.spec.js",
    "chars": 1875,
    "preview": "import React from \"react\";\nimport { renderToStaticMarkup } from \"react-dom/server\";\nimport prettier from \"prettier\";\nimp"
  },
  {
    "path": "test/specific.spec.js",
    "chars": 2337,
    "preview": "import React from \"react\";\nimport ReactTestRenderer from \"react-test-renderer\";\nimport styled from \"../src\";\n\nconst rend"
  },
  {
    "path": "test/styled.spec.js",
    "chars": 5336,
    "preview": "import React from \"react\";\nimport ReactTestRenderer from \"react-test-renderer\";\nimport styled, { css } from \"../src\";\n\nc"
  },
  {
    "path": "test/theme.spec.js",
    "chars": 1468,
    "preview": "import React from \"react\";\nimport { renderToStaticMarkup } from \"react-dom/server\";\nimport styled, { withTheme, ThemePro"
  },
  {
    "path": "test/typings.spec.tsx",
    "chars": 495,
    "preview": "// @ts-check\nimport styled from \"../\";\n\ndescribe(\"typings\", () => {\n  it(\"does not report ts error\", () => {\n    const S"
  },
  {
    "path": "tsconfig.json",
    "chars": 198,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"commonjs\",\n    \"strict\": true,\n    \"esModuleInterop\": true,"
  }
]

About this extraction

This page contains the full source code of the sergeybekrin/styled-email-components GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (46.6 KB), approximately 12.7k tokens, and a symbol index with 30 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!