I would prefer to use inline source maps in my webpack config as it sources to the actual original file.
my webpack config is...
const paths = {
src: './widgets/src/scripts/index.js',
dest: '/src/main/content/jcr_root/etc/designs/universal-template-abloyalty/clientlib/js'
};
module.exports = (env, argv) => {
return {
devtool: argv.mode === 'production' ? 'source-map' : 'inline-source-map',
entry: paths.src,
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
}
]
},
output: {
path: __dirname + paths.dest,
filename: 'widgets.js'
},
resolve: {
extensions: ['*', '.js', '.jsx']
}
};
};
and then for example in my index.js i do a simple console log...
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
initReact = () => {
function createRootElement() {
const el = document.createElement('div');
el.id = 'root';
document.body.appendChild(el);
return el;
}
const root = document.getElementById('root') || createRootElement();
ReactDOM.render(<App />, root);
console.log('test');
};
if (typeof onReactReady === 'function') onReactReady();
The problem I am having is that in my browser console, this should obviously map to index.js:16 using inline-source-map. Instead its mapping to MobileFilterComponent.js:15
Any thoughts on why this could be happening?
Interestingly enough when I use eval-source-map it maps correctly, but with an altered file name. This makes looking up a file from ctrl+p in sources a pain as you may not know how the file is named.
**note I tagged this with AEM, as this is for an AEM project. I am wondering if maybe when AEM builds clientlib.js if some how the source maps are getting corrupted.
I created my react Project A using Create-React-app. Then I bundle it them with Webpack and saved in my Git account.
Now I create another project(Called it Project B)in different directory. Download Project A directly from git. And trying to use it like so:
import React from 'react';
import ReactDOM from 'react-dom';
import { Main } from 'project-A/dist/main'
ReactDOM.render(<Main />, document.getElementById('root'));
I am getting an error like following:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
The webpack from Project A looks like this:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
module.exports = [
{
/*Client Side*/
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: { minimize: true }
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader,"css-loader"]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./public/index.html",
filename:"./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename:"[id].css"
})
]
}
]
I have research through the github and tried to change the name import, it still does not work.
Project A's component looks like this:
App.js:
render() {
return (
<div>
{this.renderCodeAuthCard()}
{this.renderConfirmCard()}
{this.renderVerifyCard()}
</div>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Apparently webpack is not exporting the bundle file that is created in Project A. Since the import yields "undefine".
I am trying to find a way to export my webpack bundle file and use it in another project.
Any help will be appreciated
Its because you are not exporting any thing from index.js of Project A. The libraries installed by npm export functions from index.js.
I am attempting SSR with React router's StaticRouter.
express.js (server)
const html = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
res.status(200).send(`
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/app.css" type="text/css"/>
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`);
Serving of static files:
app.use(express.static(path.resolve(__dirname, "../dist/client")));
App.js (shared)
import React from "react";
import { Switch, Route } from "react-router";
export default () => {
return (
<Switch>
...
</Switch>
);
};
index.jsx (client)
import React from "react";
import { BrowserRouter } from "react-router-dom";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById("app")
);
./styles/Main.scss
.header {
background-color: #002933;
}
I have 2 webpack configurations, 1 for the client & 1 for the server:
webpack.config.dev.js
const nodeExternals = require("webpack-node-externals");
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
devtool: "cheap-module-eval-source-map",
entry: {
app: [
"eventsource-polyfill",
"webpack-hot-middleware/client",
"webpack/hot/only-dev-server",
"react-hot-loader/patch",
"./client/index.jsx",
],
vendor: [
"react",
"react-dom",
],
},
output: {
path: `${__dirname}/dist/client`,
...
},
...
module: {
loaders: [
...
}, {
test: /\.scss$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
query: {
localIdentName: "[hash:8]",
modules: true
}
}, {
loader: "postcss-loader"
}, {
loader: "sass-loader"
}
]
}),
},
],
},
plugins: [
new ExtractTextPlugin({
filename: "[name].css",
allChunks: true
}),
]
};
webpack.config.server.js
const ExternalsPlugin = require("webpack-externals-plugin");
module.exports = {
...
output: {
path: `${__dirname}/dist/`,
filename: "server.bundle.js",
},
...
resolve: {
...
modules: [
"client",
],
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader",
}, {
test: /\.scss$/,
loader: 'style-loader!css-loader/locals?module&localIdentName=[name]__[local]___[hash:base64:5]!sass-loader',
},
],
},
plugins: [
new ExternalsPlugin({
type: "commonjs",
include: `${__dirname}/node_modules/`,
}),
],
};
I have a JSX file where the .header should be applied to:
import React from "react";
import Links from "./Links.jsx";
import profilePic from "../../img/brand/profilePic.jpg";
import styles from "../../styles/Main.scss";
export default class Header extends React.Component {
constructor() {
super();
}
render() {
return (
<header className={styles.header}>
<img src={profilePic} alt="Professional Picture"/>
<h5>{this.props.pageName}</h5>
<Links/>
</header>
);
}
}
This throws the error:
TypeError: Cannot read property 'header' of undefined
at Header.render (E:/Documents/Projects/website/client/js/components/Header.jsx:22:30)
at resolve (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2149:18)
at ReactDOMServerRenderer.render (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2260:22)
at ReactDOMServerRenderer.read (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2234:19)
at Object.renderToString (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2501:25)
at E:/Documents/Projects/website/server/config/lib/express.js:204:31
at Layer.handle [as handle_request] (E:\Documents\Projects\website\node_modules\express\lib\router\layer.js:95:5)
at trim_prefix (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:317:13)
at E:\Documents\Projects\website\node_modules\express\lib\router\index.js:284:7
at Function.process_params (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:335:12)
at next (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:275:10)
at p3p (E:\Documents\Projects\website\node_modules\lusca\lib\p3p.js:15:9)
at E:\Documents\Projects\website\node_modules\lusca\index.js:59:28
at xframe (E:\Documents\Projects\website\node_modules\lusca\lib\xframes.js:12:9)
at E:\Documents\Projects\website\node_modules\lusca\index.js:59:28
at xssProtection (E:\Documents\Projects\website\node_modules\lusca\lib\xssprotection.js:16:9)
When running the application, webpack reports that the stylesheet has been loaded:
EDIT
Other than an ES6 import, I have attempted to use CommonJS' require() as in MERN but still no look...
When I build my server webpack config, I am now getting the error:
ERROR in (webpack)-dev-middleware/node_modules/mime/index.js
Module not found: Error: Can't resolve './types/standard' in 'E:\Documents\Projects\website\node_modules\webpack-dev middleware\node_modules\mime'
# (webpack)-dev-middleware/node_modules/mime/index.js 4:26-53
# (webpack)-dev-middleware/index.js
# ./server/config/lib/express.js
# ./server/config/lib/app.js
# ./server/server.js
I am not sure if this a red-herring or not in this situation or not but thought it worth mentioning here as I am quite lost. Feel as though I am clutching at straws at this point.
This is my .babelrc:
{
"presets": [
"react",
"es2015",
"stage-0"
],
"plugins": [
"react-hot-loader/babel",
"transform-decorators-legacy"
],
"env": {
"server": {
"plugins": [
[
"css-modules-transform", {
"preprocessCss": "./loaders/sass-loader.js",
"generateScopedName": "[hash:8]",
"extensions": [".scss"]
}
]
]
},
"production": {
"presets": [
"es2015",
"react",
"react-optimize",
"es2015-native-modules",
"stage-0"
]
}
}
}
I was attempting to go back to basics and have my babel handle server-side bundling instead of webpack. This was built from a tutorial for SSR with CSS modules I was kindly linked to by #mootrichard
EDIT 2
A few observations which might help...when using an es6 import for stylesheets:
import styles from "../../styles/Main.scss";
and log styles into the console, it returns undefined (evidence that it cannot find the file for some reason).
When putting the <link> tag in the head for the initial page, the <link> tag is present in the markup but not in the network:
However, when navigating to localhost:8000/app.css, a positive response with the styling is sent back:
If the browser can find the bundled version standalone, then why is it not being loaded in my initial page? (The path is correct)
You're having issues because you're using css-loader/locals but not using ExtractTextPlugin (at least in Development).
https://github.com/webpack-contrib/css-loader/issues/59
Note: For prerendering with extract-text-webpack-plugin you should use css-loader/locals instead of style-loader!css-loader in the prerendering bundle. It doesn't embed CSS but only exports the identifier mappings.
This also explains why you're not able to access the style variable .theHeader.
Also, the error Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:3000/app.css". is a red herring. That is simply the error message you receive if you try to load a stylesheet that doesn't even exist. Which in this case, doesn't appear to be in the directory that you think it is, or isn't actually being generated into a file there.
Since ExtractTextPlugin is disabled in development, its likely that your CSS is only being processed by css-loader/locals. This might not be a problem in production, since it pairs with ExtractTextPlugin but could explain your problems of running this in development.
Update:
In looking into this over a little more, I came across a blog post that I think might help you figure out how to configure your CSS to work how you want. https://medium.com/#mattvagni/server-side-rendering-with-css-modules-6b02f1238eb1
I think the main reason for the complication here is that you're sending over the HTML as a rendered string via ReactDOMServer. So there is no where for webpack to inject a <link> tag into. You might want to consider just having a <link> tag in your header to reference your desired CSS file, since webpack is going to create a single CSS file anyways.
Finally, I highly recommend studying a bit more on webpack, especially since SSR is a newer process and requires doing things a bit differently than many have initially anticipated when webpack was first created.
I think you have an error in your webpack.config json hierarchy for you css/sass loaders. Replace your "loaders" array under module with this "rules" array:
module: {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'sass-loader']
}),
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
],
},
You can see more examples of ExtractTextPlugin usage here: https://github.com/webpack-contrib/extract-text-webpack-plugin#usage
I tried the very basic HMR(without react-hot-loader) using webpack after reading this Medium post by Dan Abramov.
Entry index.js
import React from 'react';
import { render } from 'react-dom';
import App from './App';
if(module.hot) {
// Basic HMR for React without using react-loader
// module.hot.accept('./App', () => {
// const UpdatedApp = require('./App').default;
//
// render(<UpdatedApp />, document.getElementById('app'));
// });
// Why does this work as well?
module.hot.accept();
}
render(<App />, document.getElementById('app'));
The webpack config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:8081',
'webpack/hot/only-dev-server',
'./src/js/index.js'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [{ loader: 'babel-loader' }]
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: 'src/index.html' }),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
],
devServer: {
hot: true
}
}
My question is that, why does HMR for React app works with just module.hot.accept()?
As per my understanding, webpack's HMR only provides a simple API to detect changes in files. How to handle those changes depends on the loaders. The style-loader handles .css files. And, in context of a React app, the changes to .js modules might be managed by react-hot-loader.
Or, as per the Medium post, one can manage them like this:
render(<UpdatedApp />, document.getElementById('app'))
So why just doing module.hot.accept() gives same result as doing render(<UpdatedApp />, document.getElementById('app'))?
Package verisons
"webpack": "^3.10.0",
"webpack-dev-server": "^2.9.7"
This is a minimal reactjs application which, when bundled using webpack and then run, gives the following error from the browser console:
Uncaught ReferenceError: snapapp is not defined
Can anyone suggest why?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body class='section'>
<div id="app"></div>
<script type="text/javascript" src="./build/snapapp.js"></script>
<script type="text/javascript">
(function () {
var mountNode = document.getElementById('app');
snapapp.start(mountNode);
})();
</script>
</body>
</html>
index.js
import { render } from 'react-dom';
import rootLayout from './src/components/ApplicationLayout.jsx';
import hello from './src/components/hello.jsx';
export function start(mountNode) {
render(<rootLayout />, mountNode);
}
webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './index.js',
output: {
path: 'build',
filename: 'snapapp.js',
libraryTarget: "umd"
},
module: {
loaders: [
{
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
]
}
};
src/components/hello.jsx
import React from 'react';
class Hello extends React.Component {
render() {
return <h1>Hello</h1>
}
}
You are exporting snapapp from your module, but you aren't importing/requiring it anywhere, it doesn't exist as a global variable.
You are exporting as a UMD file format. If you need to inline code within your index.html file then you will need to either use a library like Require
Alternative you can:
Output to var
Change umd to var, you can read about it here, this will add your library onto the window global object. Allowing you to call it like you are within your html file.
output: {
path: 'build',
filename: 'snapapp.js',
libraryTarget: "var"
}
Place your bootup script into your file bundle
Unless there is some special reason I would move the bootup script into your index.js file. There is no reason you can not get the mount point from within the file. This also a very common pattern for what you are trying to do.
import { render } from 'react-dom';
import rootLayout from './src/components/ApplicationLayout.jsx';
import hello from './src/components/hello.jsx';
render(<rootLayout />, document.getElementById('app'));