diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..1297a0e --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015", "react", "stage-0"], + "plugins": ["add-module-exports"] +} diff --git a/.gitignore b/.gitignore index 947dcde..090ac6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .idea/ node_modules/ +build/ dist/ + *.log *.map .DS_Store diff --git a/.npmignore b/.npmignore index c9c2908..620c0a8 100644 --- a/.npmignore +++ b/.npmignore @@ -6,7 +6,6 @@ dist/ .DS_Store static/ -src/example/ -src/example.* +src/ tmp/ webpack.*.js diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/README.md b/README.md index 0a57737..6bac638 100644 --- a/README.md +++ b/README.md @@ -53,19 +53,19 @@ You get namespaced CSS that works on sub-components (comparable to HTML5 ` @@ -79,7 +79,7 @@ For a cascaded effect, see the `index.html` demo. ## Usage -Run `npm run watch` in your terminal and play with `example/` to get a feel of react-inline-css. +Run `npm run devserver` in your terminal and play with `example/` to get a feel of react-inline-css. ### SASS / LESS @@ -127,11 +127,12 @@ UserComponent { ## Community Let's start one together! After you ★Star this project, follow me [@Rygu](https://twitter.com/rygu) -on Twitter. +on Twitter. ### Contributors - [Danilo Moret](https://github.com/moret) +- [Luigi Poole](https://github.com/luigiplr) ## License diff --git a/package.json b/package.json index 7af7f07..1386291 100644 --- a/package.json +++ b/package.json @@ -15,24 +15,31 @@ "react-component", "style" ], - "main": "src/react-inline-css", + "main": "build/react-inline-css.js", "scripts": { - "localhost": "sleep 2; which open && open http://localhost:8080", - "build": "webpack --verbose --colors --display-error-details --config webpack.client.js", - "watch-client": "webpack --verbose --colors --display-error-details --config webpack.client-watch.js && webpack-dev-server --config webpack.client-watch.js", - "watch": "concurrent 'npm run watch-client' 'npm run localhost'" + "babel": "mkdirp build && babel src/react-inline-css.js -o build/react-inline-css.js", + "build": "webpack -p --verbose --colors --display-error-details && npm run babel", + "devserver": "webpack-dev-server --config webpack.config.devServer.babel.js", + "prepublish": "npm run babel" + }, + "dependencies": { + "react": "^15.2.1", + "react-dom": "^15.2.1" }, "devDependencies": { - "babel-core": "6.7.6", + "babel-cli": "^6.10.1", + "babel-core": "^6.10.4", "babel-loader": "6.2.4", - "babel-preset-es2015": "6.6.0", - "babel-preset-react": "6.5.0", - "concurrently": "2.0.0", + "babel-plugin-add-module-exports": "^0.2.1", + "babel-preset-es2015": "^6.9.0", + "babel-preset-react": "^6.11.1", + "babel-preset-stage-0": "^6.5.0", + "html-webpack-plugin": "^2.22.0", + "html-webpack-template": "^5.0.0", "json-loader": "0.5.4", - "react": "15.0.1", - "react-dom": "15.0.1", - "react-hot-loader": "1.3.0", - "webpack": "1.13.0", + "mkdirp": "^0.5.1", + "open-browser-webpack-plugin": "0.0.2", + "webpack": "^1.13.1", "webpack-dev-server": "1.14.1" } } diff --git a/src/react-inline-css.js b/src/react-inline-css.js index 570fbc4..68b5589 100644 --- a/src/react-inline-css.js +++ b/src/react-inline-css.js @@ -1,58 +1,50 @@ /** * @copyright © 2015, Rick Wong. All rights reserved. */ -var React = require("react"); -var assign = Object.assign ? Object.assign : React.__spread; -var refCounter = 0; +import React, { PropTypes, Component } from 'react' /** * @module InlineCss */ -var InlineCss = React.createClass({ - displayName: "InlineCss", - propTypes: { - namespace: React.PropTypes.string, - componentName: React.PropTypes.string, - stylesheet: React.PropTypes.string.isRequired, - className: React.PropTypes.string, - wrapper: React.PropTypes.string - }, - _transformSheet: function (stylesheet, componentName, namespace) { - return stylesheet. - // Prettier output. - replace(/}\s*/ig, '\n}\n'). - // Regular rules are namespaced. - replace( - /(^|{|}|;|,)\s*([&a-z0-9\-_\.:#\(\),>*\s]+)\s*(\{)/ig, - function (matched) { - return matched.replace(new RegExp(componentName, "g"), "#" + namespace); - } - ); - }, - render: function () { - var namespace = this.props.namespace || "InlineCss-" + refCounter++; - var componentName = this.props.componentName || "&"; - var stylesheet = this._transformSheet(this.props.stylesheet, componentName, namespace); - var Wrapper = this.props.wrapper || "div"; - var wrapperProps = assign({}, this.props, { - namespace: undefined, - componentName: undefined, - stylesheet: undefined, - wrapper: undefined, - id: namespace - }); +export default class InlineCss extends Component { + static propTypes = { + namespace: PropTypes.string, + componentName: PropTypes.string, + stylesheet: PropTypes.string.isRequired, + wrapper: PropTypes.string, + children: PropTypes.any + } - return React.createElement( - Wrapper, - wrapperProps, - this.props.children, - React.createElement("style", { - scoped: true, - dangerouslySetInnerHTML: {__html: stylesheet} - }) - ); - } -}); + static defaultProps = { + namespace: 'inlineCss', + componentName: '&', + wrapper: 'div' + } -module.exports = InlineCss; + _transformSheet(stylesheet, componentName, namespace) { + if (this._cachedSheet && this._cachedSheet.formatted && this._cachedSheet.stylesheet === stylesheet) { + return this._cachedSheet.formatted + } + + const formatted = stylesheet.replace(/([^\r\n,{}]+)(,(?=[^}]*{)|\s*{)/ig, matched => matched.replace(new RegExp(componentName, 'g'), '#' + namespace)) + this._cachedSheet = { stylesheet, formatted } + return formatted + } + + render() { + const { namespace, componentName, stylesheet, wrapper, ...wrapperProps } = this.props + const id = namespace !== 'inlineCss' ? namespace : `${namespace}-${(Math.random().toString(36) + '00000000000000000').slice(2, 7 + 2)}` + + return React.createElement( + wrapper, { id, ...wrapperProps }, + this.props.children, + React.createElement('style', { + scoped: true, + dangerouslySetInnerHTML: { + __html: ::this._transformSheet(stylesheet, componentName, id) + } + }) + ) + } +} diff --git a/static/index.html b/static/index.html deleted file mode 100644 index e80f94b..0000000 --- a/static/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - react-inline-style - - -
- - - diff --git a/webpack.client-watch.js b/webpack.client-watch.js deleted file mode 100644 index 810aef3..0000000 --- a/webpack.client-watch.js +++ /dev/null @@ -1,34 +0,0 @@ -var webpack = require("webpack"); -var config = require("./webpack.client.js"); - -config.cache = true; -config.debug = true; -config.devtool = "eval"; - -config.entry.WDS = "webpack-dev-server/client?http://localhost:8080"; -config.entry.hot = "webpack/hot/only-dev-server"; - -config.module.postLoaders = [ - {test: /\.js$/, loaders: ["react-hot"], exclude: /node_modules/} -]; - -config.output.publicPath = "http://localhost:8080/dist/"; -config.output.hotUpdateMainFilename = "update/[hash]/update.json"; -config.output.hotUpdateChunkFilename = "update/[hash]/[id].update.js"; - -config.plugins = [ - new webpack.HotModuleReplacementPlugin() -]; - -config.devServer = { - publicPath: "http://localhost:8080/dist/", - contentBase: "./static", - hot: true, - inline: true, - quiet: true, - noInfo: true, - headers: {"Access-Control-Allow-Origin": "*"}, - stats: {colors: true} -}; - -module.exports = config; diff --git a/webpack.client.js b/webpack.client.js deleted file mode 100644 index 628b2c6..0000000 --- a/webpack.client.js +++ /dev/null @@ -1,32 +0,0 @@ -var webpack = require("webpack"); -var path = require("path"); - -module.exports = { - target: "web", - cache: false, - context: __dirname, - devtool: false, - entry: {example:"./src/example"}, - output: { - path: path.join(__dirname, "static/dist"), - filename: "[name].js", - chunkFilename: "[name].[id].js", - publicPath: "dist/" - }, - plugins: [ - new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}}), - new webpack.optimize.DedupePlugin(), - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.optimize.UglifyJsPlugin() - ], - module: { - loaders: [ - {test: /\.json$/, loaders: ["json-loader"]}, - {test: /\.js$/, loaders: ["babel-loader?presets[]=es2015&presets[]=react"], exclude: /node_modules/}, - {test: /\.scss$/, loaders: ["raw-loader", "sass-loader"], exclude: /node_modules/} - ] - }, - resolve: { - extensions: ["", ".json", ".jsx", ".js"] - } -}; diff --git a/webpack.config.babel.js b/webpack.config.babel.js new file mode 100644 index 0000000..6dc8feb --- /dev/null +++ b/webpack.config.babel.js @@ -0,0 +1,28 @@ +import { optimize, DefinePlugin } from 'webpack' +import { join } from 'path' + +export default { + target: 'web', + cache: false, + context: __dirname, + devtool: false, + entry: ['./src/react-inline-css', './src/example'], + output: { + path: join(__dirname, 'dist'), + filename: 'react-inline-css.bundle.js' + }, + plugins: [ + new DefinePlugin({ 'process.env': { NODE_ENV: 'production' } }), + new optimize.DedupePlugin(), + new optimize.OccurenceOrderPlugin(), + new optimize.UglifyJsPlugin({ output: { comments: false } }) + ], + module: { + loaders: [ + { test: /\.json$/, loaders: ['json-loader'] }, + { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }, + { test: /\.scss$/, loaders: ['raw-loader', 'sass-loader'], exclude: /node_modules/ } + ] + }, + resolve: { extensions: ['', '.json', '.jsx', '.js'] } +} diff --git a/webpack.config.devServer.babel.js b/webpack.config.devServer.babel.js new file mode 100644 index 0000000..8fcde1e --- /dev/null +++ b/webpack.config.devServer.babel.js @@ -0,0 +1,36 @@ +import { HotModuleReplacementPlugin } from 'webpack' +import OpenBrowserPlugin from 'open-browser-webpack-plugin' +import HtmlWebpackPlugin from 'html-webpack-plugin' +import { resolve } from 'path' +import webpackConfig from './webpack.config.babel' + +export default { + devServer: { + historyApiFallback: true, + hot: true, + progress: true, + contentBase: resolve('static'), + port: 8080, + outputPath: resolve('build'), + compress: true + }, + ...webpackConfig, + devtool: 'inline-source-map', + plugins: [ + new HotModuleReplacementPlugin(), + new OpenBrowserPlugin({ url: 'http://localhost:8080' }), + new HtmlWebpackPlugin({ + inject: false, + template: require('html-webpack-template'), + appMountId: 'react-root', + title: 'react-inline-style' + }) + ], + entry: [ + 'webpack/hot/dev-server', + 'webpack-dev-server/client?http://localhost:8080', + './src/example' + ], + debug: true, + cache: true +}