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"