Created
November 27, 2017 21:42
-
-
Save kevinguard/8d702ddd3188fb667faedd83ab9d8205 to your computer and use it in GitHub Desktop.
Webpack + React + Loading CSS of external modules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require("fs"); | |
const externalModules = fs.readdirSync("node_modules") | |
.filter(x => [".bin"].indexOf(x) === -1) | |
.reduce((acc, cur) => Object.assign(acc, { [cur]: "commonjs " + cur }), {}); | |
module.exports = externalModules; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require("path"); | |
/* | |
* __dirname is changed after webpack-ed to another directory | |
* so process.cwd() is used instead to determine the correct base directory | |
* Read more: https://nodejs.org/api/process.html#process_process_cwd | |
*/ | |
const CURRENT_WORKING_DIR = process.cwd(); | |
module.exports = { | |
app: path.resolve(CURRENT_WORKING_DIR, "app"), | |
assets: path.resolve(CURRENT_WORKING_DIR, "public", "assets"), | |
compiled: path.resolve(CURRENT_WORKING_DIR, "compiled"), | |
public: "/assets/", // use absolute path for css-loader? | |
modules: path.resolve(CURRENT_WORKING_DIR, "node_modules"), | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const webpack = require("webpack"); | |
const ExtractTextPlugin = require("extract-text-webpack-plugin"); | |
const ManifestPlugin = require("webpack-manifest-plugin"); | |
module.exports = ({ production = false, browser = false } = {}) => { | |
const bannerOptions = {raw: true, banner: 'require("source-map-support").install();'}; | |
const compress = {warnings: false}; | |
if (!production && !browser) { | |
return [ | |
new webpack.EnvironmentPlugin(["NODE_ENV"]), | |
new webpack.DefinePlugin({ | |
__PRODUCTION__: JSON.stringify(production), | |
__DEVCLIENT__ : false, | |
__DEVSERVER__ : true, | |
}), | |
new webpack.BannerPlugin(bannerOptions), | |
]; | |
} | |
if (!production && browser) { | |
return [ | |
new webpack.EnvironmentPlugin(["NODE_ENV"]), | |
new webpack.DefinePlugin({ | |
__PRODUCTION__: JSON.stringify(production), | |
__DEVCLIENT__ : true, | |
__DEVSERVER__ : false, | |
}), | |
new webpack.HotModuleReplacementPlugin(), | |
new webpack.NoEmitOnErrorsPlugin(), | |
]; | |
} | |
if (production && !browser) { | |
return [ | |
new webpack.EnvironmentPlugin(["NODE_ENV"]), | |
new webpack.DefinePlugin({ | |
__PRODUCTION__: JSON.stringify(production), | |
__DEVCLIENT__ : false, | |
__DEVSERVER__ : false, | |
}), | |
new webpack.BannerPlugin(bannerOptions), | |
new webpack.optimize.UglifyJsPlugin({compress}), | |
]; | |
} | |
if (production && browser) { | |
return [ | |
new webpack.EnvironmentPlugin(["NODE_ENV"]), | |
new webpack.DefinePlugin({ | |
__PRODUCTION__: JSON.stringify(production), | |
__DEVCLIENT__ : false, | |
__DEVSERVER__ : false, | |
}), | |
new ExtractTextPlugin({ | |
filename: "[contenthash].css", | |
allChunks: true | |
}), | |
new webpack.optimize.UglifyJsPlugin({compress}), | |
new ManifestPlugin({ | |
fileName: "manifest.json", | |
}), | |
]; | |
} | |
return []; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const PATHS = require("./paths"); | |
module.exports = { | |
modules: [PATHS.app, PATHS.modules], | |
extensions: [".js", ".jsx", ".css"], | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require("path"); | |
const ExtractTextPlugin = require("extract-text-webpack-plugin"); | |
const postcssImport = require("postcss-import"); | |
const postcssCssnext = require("postcss-cssnext"); | |
const postcssReporter = require("postcss-reporter"); | |
const PATHS = require("../paths"); | |
module.exports = ({ production = false, browser = false } = {}) => { | |
/* | |
* modules: boolean - Enable/Disable CSS Modules | |
* importLoaders: number - Number of loaders applied before CSS loader | |
* | |
* Read more about css-loader options | |
* https://webpack.js.org/loaders/css-loader/#options | |
* | |
* For server-side rendering we use css-loader/locals as we do not want to | |
* embed CSS. However, we still require the mappings to insert as className in | |
* our views. | |
* | |
* Referenced from: https://github.com/webpack-contrib/css-loader#css-scope | |
* | |
* For pre-rendering with extract-text-webpack-plugin you should use | |
* css-loader/locals instead of style-loader!css-loader in the pre-rendering bundle. | |
* It doesn't embed CSS but only exports the identifier mappings. | |
*/ | |
const localIndentName = "localIdentName=[name]__[local]___[hash:base64:5]"; | |
const createCssLoaders = embedCssInBundle => ([ | |
{ | |
loader: embedCssInBundle ? "css-loader" : "css-loader/locals", | |
options: { | |
localIndentName, | |
sourceMap: true, | |
modules: true, | |
importLoaders: 1, | |
}, | |
}, | |
{ | |
loader: "postcss-loader", | |
options: { | |
plugins: [ | |
postcssImport({path: path.resolve(PATHS.app, "./css")}), | |
postcssCssnext({browsers: ["> 1%", "last 2 versions"]}), | |
postcssReporter({clearMessages: true}), | |
], | |
}, | |
}, | |
]); | |
const createBrowserLoaders = extractCssToFile => loaders => { | |
if (extractCssToFile) { | |
return ExtractTextPlugin.extract({ | |
fallback: "style-loader", | |
use: loaders, | |
}); | |
} | |
return [ | |
{ | |
loader: "style-loader", | |
}, | |
...loaders, | |
]; | |
}; | |
const serverLoaders = createCssLoaders(false); | |
const browserLoaders = createBrowserLoaders(production)(createCssLoaders(true)); | |
return { | |
test: /\.css$/, | |
use: browser ? browserLoaders : serverLoaders, | |
include: PATHS.app, | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const javascript = require("./javascript"); | |
const css = require("./css"); | |
module.exports = ({production = false, browser = false} = {}) => ( | |
[ | |
javascript({ production, browser }), | |
css({ production, browser }), | |
] | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const PATHS = require("../paths"); | |
module.exports = ({production = false, browser = false} = {}) => { | |
const enableHotModuleReplacement = !production && browser; | |
const createPresets = enableHotModuleReplacement => { | |
const presets = ["env", "react", "stage-0"]; | |
return enableHotModuleReplacement ? ["react-hmre", ...presets] : presets; | |
}; | |
const presets = createPresets(enableHotModuleReplacement); | |
const plugins = production ? [ | |
"transform-react-remove-prop-types", | |
"transform-react-constant-elements", | |
"transform-react-inline-elements", | |
] : []; | |
return { | |
test: /\.js$|\.jsx$/, | |
loader: "babel-loader", | |
options: { | |
presets, | |
plugins, | |
}, | |
exclude: PATHS.modules, | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* process.env.NODE_ENV - used to determine whether we generate a production or development bundle | |
* | |
* webpack --env.browser - used to determine whether to generate a browser or server bundle | |
* | |
* NOTE: browser/server is client/server-side rendering respectively in universal/isomorphic javascript | |
*/ | |
const PATHS = require("./paths"); | |
const rules = require("./rules"); | |
const plugins = require("./plugins"); | |
const externals = require("./externals"); | |
const resolve = require("./resolve"); | |
module.exports = (env = {}) => { | |
const isProduction = process.env.NODE_ENV === "production"; | |
const isBrowser = env.browser; | |
console.log(`Running webpack in ${process.env.NODE_ENV} mode on ${isBrowser ? "browser" : "server"}`); | |
const hotMiddlewareScript = "webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true"; | |
const node = { | |
__dirname: true, | |
__filename: true | |
}; | |
const prodServerRender = { | |
devtool: "source-map", | |
context: PATHS.app, | |
entry: { server: "../server/index" }, | |
target: "node", | |
node, | |
externals, | |
output: { | |
path: PATHS.compiled, | |
filename: "[name].js", | |
publicPath: PATHS.public, | |
libraryTarget: "commonjs2" | |
}, | |
module: { rules: rules({ production: true, browser: false }) }, | |
resolve, | |
plugins: plugins({ production: true, browser: false }) | |
}; | |
const prodBrowserRender = { | |
devtool: "cheap-module-source-map", | |
context: PATHS.app, | |
entry: { app: ["./client"] }, | |
node, | |
output: { | |
path: PATHS.assets, | |
filename: "[chunkhash].js", | |
chunkFilename: "[name].[chunkhash:6].js", // for code splitting. will work without but useful to set | |
publicPath: PATHS.public | |
}, | |
module: { rules: rules({ production: true, browser: true }) }, | |
resolve, | |
plugins: plugins({ production: true, browser: true }) | |
}; | |
const devBrowserRender = { | |
devtool: "eval", | |
context: PATHS.app, | |
entry: { app: ["./client", hotMiddlewareScript] }, | |
node, | |
output: { | |
path: PATHS.assets, | |
filename: "[name].js", | |
publicPath: PATHS.public | |
}, | |
module: { rules: rules({ production: false, browser: true }) }, | |
resolve, | |
plugins: plugins({ production: false, browser: true }), | |
}; | |
const devServerRender = { | |
devtool: "sourcemap", | |
context: PATHS.app, | |
entry: { server: "../server/index" }, | |
target: "node", | |
node, | |
externals, | |
output: { | |
path: PATHS.compiled, | |
filename: "[name].dev.js", | |
publicPath: PATHS.public, | |
libraryTarget: "commonjs2", | |
}, | |
module: { rules: rules({ production: false, browser: false }) }, | |
resolve, | |
plugins: plugins({ production: false, browser: false }), | |
}; | |
const prodConfig = isBrowser ? prodBrowserRender : prodServerRender; | |
const devConfig = isBrowser ? devBrowserRender : devServerRender; | |
return isProduction ? prodConfig : devConfig; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment