diff --git a/.gitignore b/.gitignore
index 6748883..d6a8d4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,9 @@ node_modules
/*/*/es
/*/*/build
/*/*/coverage
+addons/styled-components/.DS_Store
+addons/.DS_Store
+yarn.lock
+yarn-error.log
+yarn.lock
+yarn.lock
diff --git a/addons/styled-components/neutrino.js b/addons/styled-components/neutrino.js
new file mode 100644
index 0000000..9ea6e4a
--- /dev/null
+++ b/addons/styled-components/neutrino.js
@@ -0,0 +1 @@
+module.exports = require('./lib/neutrino').default
diff --git a/addons/styled-components/package.json b/addons/styled-components/package.json
new file mode 100644
index 0000000..516a966
--- /dev/null
+++ b/addons/styled-components/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "tux-addon-styled-components",
+ "version": "0.4.0",
+ "description": "Use Styled Components on the server and client.",
+ "bugs": {
+ "url": "https://github.com/aranja/tux/issues"
+ },
+ "repository": "aranja/tux",
+ "keywords": ["react", "styled-components", "tux-addon"],
+ "main": "lib/index.js",
+ "module": "es/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "prepublish": "NODE_ENV=production ../../tasks/build-package.js",
+ "watch": "../../tasks/build-package.js --watch",
+ "test": "../../node_modules/.bin/jest"
+ },
+ "peerDependencies": {
+ "react": ">16.0.0-beta",
+ "react-chain": "^0.5.0"
+ },
+ "files": ["es", "lib"],
+ "license": "MIT",
+ "devDependencies": {
+ "jest": "^18.1.0",
+ "react": "^16.0.0",
+ "react-dom": "^16.0.0",
+ "styled-components": "^3.3.3",
+ "babel-plugin-styled-components": "^1.5.1"
+ }
+}
diff --git a/addons/styled-components/src/index.ts b/addons/styled-components/src/index.ts
new file mode 100644
index 0000000..1e2b5a6
--- /dev/null
+++ b/addons/styled-components/src/index.ts
@@ -0,0 +1,18 @@
+import { Middleware } from 'react-chain'
+import { ServerStyleSheet } from 'styled-components'
+
+const styled = (): Middleware => session => {
+ if (!process.env.BROWSER && session.req) {
+ const sheet = new ServerStyleSheet()
+ session.on('server', render => {
+ render()
+ const styles = sheet.getStyleElement()
+ session.document.head.push(...styles)
+ })
+
+ return async next => sheet.collectStyles(await next())
+ }
+ return undefined
+}
+
+export default styled
diff --git a/addons/styled-components/src/neutrino.ts b/addons/styled-components/src/neutrino.ts
new file mode 100644
index 0000000..f446c9b
--- /dev/null
+++ b/addons/styled-components/src/neutrino.ts
@@ -0,0 +1,27 @@
+import { Neutrino } from 'neutrino'
+
+const { merge } = require('@neutrinojs/compile-loader')
+
+const styledMiddleware = (neutrino: Neutrino, opts = {}) => {
+ const options = Object.assign(
+ {
+ ssr: true,
+ displayName: process.env.NODE_ENV !== 'production',
+ },
+ opts
+ )
+
+ // prettier-ignore
+ neutrino.config.module
+ .rule('compile')
+ .use('babel')
+ .tap(babelOptions =>
+ merge(babelOptions, {
+ plugins: [
+ [require.resolve('babel-plugin-styled-components'), options],
+ ],
+ })
+ )
+}
+
+module.exports = styledMiddleware
diff --git a/addons/styled-components/tsconfig.json b/addons/styled-components/tsconfig.json
new file mode 100644
index 0000000..46b25c5
--- /dev/null
+++ b/addons/styled-components/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../shared/tsconfig.base.json",
+ "compilerOptions": {
+ },
+ "include": [
+ "./src",
+ "../../shared/types"
+ ]
+}
diff --git a/examples/.DS_Store b/examples/.DS_Store
new file mode 100644
index 0000000..9802d96
Binary files /dev/null and b/examples/.DS_Store differ
diff --git a/examples/styled-components-ssr/.gitignore b/examples/styled-components-ssr/.gitignore
new file mode 100644
index 0000000..6c96c5c
--- /dev/null
+++ b/examples/styled-components-ssr/.gitignore
@@ -0,0 +1,15 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+node_modules
+
+# testing
+coverage
+
+# production
+build
+
+# misc
+.DS_Store
+.env
+npm-debug.log
diff --git a/examples/styled-components-ssr/.neutrinorc.js b/examples/styled-components-ssr/.neutrinorc.js
new file mode 100644
index 0000000..6da176a
--- /dev/null
+++ b/examples/styled-components-ssr/.neutrinorc.js
@@ -0,0 +1,12 @@
+const { version: appVersion } = require('./package.json')
+
+module.exports = {
+ options: {
+ serverEntry: 'server',
+ },
+ use: [
+ 'tux/neutrino',
+ // Styled components
+ 'tux-addon-styled-components/neutrino',
+ ],
+}
diff --git a/examples/styled-components-ssr/package.json b/examples/styled-components-ssr/package.json
new file mode 100644
index 0000000..78b1a8e
--- /dev/null
+++ b/examples/styled-components-ssr/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "tux-example-styled-components-ssr",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "dependencies": {
+ "react": "^16.0.0",
+ "react-dom": "^16.0.0",
+ "styled-components": "^3.3.3",
+ "tux": "^0.5.0"
+ },
+ "devDependencies": {
+ "babel-plugin-styled-components": "^1.5.1",
+ "tux-scripts": "0.5.3"
+ },
+ "prettier": {
+ "semi": false,
+ "singleQuote": true,
+ "trailingComma": "es5"
+ },
+ "scripts": {
+ "start": "tux-scripts start",
+ "build": "tux-scripts build",
+ "serve": "tux-scripts serve"
+ }
+}
diff --git a/examples/styled-components-ssr/src/app.js b/examples/styled-components-ssr/src/app.js
new file mode 100644
index 0000000..9d668c3
--- /dev/null
+++ b/examples/styled-components-ssr/src/app.js
@@ -0,0 +1,15 @@
+import React from 'react'
+import { createApp } from 'tux'
+import styled from 'tux-addon-styled-components'
+import Home from './components/Home'
+
+// Create a Tux app.
+const app = createApp()
+
+// SSR middleware for styled components.
+app.use(styled())
+
+// Lastly, serve a component on client and server.
+app.use()
+
+export default app
diff --git a/examples/styled-components-ssr/src/components/Description.js b/examples/styled-components-ssr/src/components/Description.js
new file mode 100644
index 0000000..f4af047
--- /dev/null
+++ b/examples/styled-components-ssr/src/components/Description.js
@@ -0,0 +1,11 @@
+import styled from 'styled-components'
+
+const Description = styled.p`
+ color: rgba(0, 0, 0, 0.75);
+ font-size: 2rem;
+ font-weight: 300;
+ margin: 0;
+ text-align: center;
+`
+
+export default Description
diff --git a/examples/styled-components-ssr/src/components/Header.js b/examples/styled-components-ssr/src/components/Header.js
new file mode 100644
index 0000000..56c6401
--- /dev/null
+++ b/examples/styled-components-ssr/src/components/Header.js
@@ -0,0 +1,23 @@
+import React, { Component } from 'react'
+import styled from 'styled-components'
+
+const HeaderContainer = styled.div`
+ position: fixed;
+ display: flex;
+ align-items: ceneter;
+ justify-content: center;
+ top: 0;
+ width: 100%;
+ padding: 1.5em;
+ text-align: center;
+ background: #f9f9f9;
+ border-bottom: 1px solid #ccc;
+`
+
+export class Header extends Component {
+ render() {
+ return Tux
+ }
+}
+
+export default Header
diff --git a/examples/styled-components-ssr/src/components/Home.js b/examples/styled-components-ssr/src/components/Home.js
new file mode 100644
index 0000000..270bb88
--- /dev/null
+++ b/examples/styled-components-ssr/src/components/Home.js
@@ -0,0 +1,38 @@
+import React from 'react'
+import styled, { injectGlobal } from 'styled-components'
+import Header from './Header'
+import TuxLogo from './TuxLogo/'
+import Description from './Description'
+
+injectGlobal`
+ html {
+ font-family: sans-serif;
+ font-size: 16px;
+ line-height: 1.5;
+ }
+ body {
+ margin: 0rem;
+ background: #fff;
+ }
+`
+
+const HomeContainer = styled.div`
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ justify-content: center;
+ margin-top: 5rem;
+`
+
+const Home = () => {
+ return (
+
+
+
+ Edit app.js to get started
+
+ )
+}
+
+export default Home
diff --git a/examples/styled-components-ssr/src/components/TuxLogo/TuxLogo.js b/examples/styled-components-ssr/src/components/TuxLogo/TuxLogo.js
new file mode 100644
index 0000000..d891f46
--- /dev/null
+++ b/examples/styled-components-ssr/src/components/TuxLogo/TuxLogo.js
@@ -0,0 +1,17 @@
+import React, { Component } from 'react'
+import styled from 'styled-components'
+import tuxLogo from './tux.svg'
+
+const LogoImg = styled.img`
+ display: block;
+ margin: 0 auto;
+ width: 25rem;
+`
+
+export class TuxLogo extends Component {
+ render() {
+ return
+ }
+}
+
+export default TuxLogo
diff --git a/examples/styled-components-ssr/src/components/TuxLogo/index.js b/examples/styled-components-ssr/src/components/TuxLogo/index.js
new file mode 100644
index 0000000..6d8fba8
--- /dev/null
+++ b/examples/styled-components-ssr/src/components/TuxLogo/index.js
@@ -0,0 +1 @@
+export { default } from './TuxLogo'
diff --git a/examples/styled-components-ssr/src/components/TuxLogo/tux.svg b/examples/styled-components-ssr/src/components/TuxLogo/tux.svg
new file mode 100644
index 0000000..dc399e9
--- /dev/null
+++ b/examples/styled-components-ssr/src/components/TuxLogo/tux.svg
@@ -0,0 +1,5 @@
+
diff --git a/examples/styled-components-ssr/src/server/index.js b/examples/styled-components-ssr/src/server/index.js
new file mode 100644
index 0000000..2540b34
--- /dev/null
+++ b/examples/styled-components-ssr/src/server/index.js
@@ -0,0 +1,16 @@
+import { serve, buildAssets } from "tux/server";
+import Document from "react-document";
+import app from "../app";
+import express from "express";
+
+export default ({ clientStats }) => {
+ const expressApp = express();
+ expressApp.use(
+ serve({
+ Document,
+ assets: buildAssets(clientStats),
+ app
+ })
+ );
+ return expressApp;
+};
diff --git a/yarn.lock b/yarn.lock
index 0e716d5..4c9f3f3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,20 @@
# yarn lockfile v1
+"@babel/helper-annotate-as-pure@^7.0.0-beta.37":
+ version "7.0.0-beta.51"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.51.tgz#38cf7920bf5f338a227f754e286b6fbadee04b58"
+ dependencies:
+ "@babel/types" "7.0.0-beta.51"
+
+"@babel/types@7.0.0-beta.51":
+ version "7.0.0-beta.51"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.51.tgz#d802b7b543b5836c778aa691797abf00f3d97ea9"
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.5"
+ to-fast-properties "^2.0.0"
+
"@gulp-sourcemaps/identity-map@1.X":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-1.0.1.tgz#cfa23bc5840f9104ce32a65e74db7e7a974bbee1"
@@ -1139,6 +1153,14 @@ babel-plugin-minify-type-constructors@^0.2.0:
dependencies:
babel-helper-is-void-0 "^0.2.0"
+babel-plugin-styled-components@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.5.1.tgz#31dbeb696d1354d1585e60d66c7905f5e474afcd"
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0-beta.37"
+ babel-types "^6.26.0"
+ stylis "^3.0.0"
+
babel-plugin-syntax-async-functions@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
@@ -1954,6 +1976,13 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
+buffer@^5.0.3:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.1.0.tgz#c913e43678c7cb7c8bd16afbcddb6c5505e8f9fe"
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@@ -2725,6 +2754,10 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
+css-color-keywords@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
+
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@@ -2785,6 +2818,14 @@ css-selector-tokenizer@^0.7.0:
fastparse "^1.1.1"
regexpu-core "^1.0.0"
+css-to-react-native@^2.0.3:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.2.1.tgz#7f3f4c95de65501b8720c87bf0caf1f39073b88e"
+ dependencies:
+ css-color-keywords "^1.0.0"
+ fbjs "^0.8.5"
+ postcss-value-parser "^3.3.0"
+
css-tree@1.0.0-alpha25:
version "1.0.0-alpha25"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha25.tgz#1bbfabfbf6eeef4f01d9108ff2edd0be2fe35597"
@@ -3914,6 +3955,18 @@ fbjs@^0.8.16, fbjs@^0.8.9:
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
+fbjs@^0.8.5:
+ version "0.8.17"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.18"
+
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
@@ -4870,6 +4923,10 @@ hoek@4.x.x:
version "4.2.0"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
+hoist-non-react-statics@^2.5.0:
+ version "2.5.5"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
+
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -6670,6 +6727,10 @@ lodash@^3.6.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+lodash@^4.17.5:
+ version "4.17.10"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -8424,6 +8485,10 @@ react-icons@^2.2.7:
dependencies:
react-icon-base "2.1.0"
+react-is@^16.3.1:
+ version "16.4.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
+
react-portal@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-3.2.0.tgz#4224e19b2b05d5cbe730a7ba0e34ec7585de0043"
@@ -9628,6 +9693,21 @@ style-loader@^0.19.0:
loader-utils "^1.0.2"
schema-utils "^0.3.0"
+styled-components@^3.3.3:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.3.3.tgz#09e702055ab11f7a8eab8229b1c0d0b855095686"
+ dependencies:
+ buffer "^5.0.3"
+ css-to-react-native "^2.0.3"
+ fbjs "^0.8.16"
+ hoist-non-react-statics "^2.5.0"
+ is-plain-object "^2.0.1"
+ prop-types "^15.5.4"
+ react-is "^16.3.1"
+ stylis "^3.5.0"
+ stylis-rule-sheet "^0.0.10"
+ supports-color "^3.2.3"
+
styled-jsx@^0.5.4:
version "0.5.7"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-0.5.7.tgz#2cb02263ffa719b1435a864fdd6c62802ae86669"
@@ -9641,6 +9721,14 @@ styled-jsx@^0.5.4:
source-map "0.5.6"
string-hash "1.1.1"
+stylis-rule-sheet@^0.0.10:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430"
+
+stylis@^3.0.0, stylis@^3.5.0:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.1.tgz#fd341d59f57f9aeb412bc14c9d8a8670b438e03b"
+
sum-up@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sum-up/-/sum-up-1.0.3.tgz#1c661f667057f63bcb7875aa1438bc162525156e"
@@ -9922,6 +10010,10 @@ to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+
to-integer-x@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/to-integer-x/-/to-integer-x-3.0.0.tgz#9f3b80e668c7f0ae45e6926b40d95f52c1addc74"
@@ -10138,6 +10230,10 @@ typescript@^2.4.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
+ua-parser-js@^0.7.18:
+ version "0.7.18"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
+
ua-parser-js@^0.7.9:
version "0.7.17"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"