So, I am making an app with React, and I am deploying it to Heroku afterwards. Everything works great in localhost and without any errors, and webpack also compiles with no errors. I have set env variales on Heroku as well.
However, when I deploy it to Heroku I get a blank screen and this error in console(I am requiring TodoApi properly and have tried various ways - import etc., it works in localhost with no issues):
I am trying to figure out it for days, so any help is much appreciated.
Uncaught Error: Cannot find module "TodoApi"
at bundle.js:20
at Object.<anonymous> (bundle.js:20)
at t (bundle.js:1)
at Object.<anonymous> (bundle.js:20)
at t (bundle.js:1)
at Object.<anonymous> (bundle.js:3)
at Object.<anonymous> (bundle.js:3)
at t (bundle.js:1)
at Object.<anonymous> (bundle.js:1)
at t (bundle.js:1)
Here are my webpack and package.json files:
var webpack = require('webpack');
var path = require('path');
var envFile = require('node-env-file');
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
try {
envFile(path.join(__dirname, 'config/' + process.env.NODE_ENV + '.env'));
} catch(e) {
}
module.exports = {
entry: [
'script!jquery/dist/jquery.min.js',
'script!foundation-sites/dist/js/foundation.min.js',
'./app/app.jsx'
],
externals: {
jquery: 'jQuery'
},
plugins: [
new webpack.ProvidePlugin({
'$': 'jquery',
'jQuery': 'jquery'
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
warnings: false
}
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
API_KEY: JSON.stringify(process.env.API_KEY),
AUTH_DOMAIN: JSON.stringify(process.env.AUTH_DOMAIN),
DATABASE_URL: JSON.stringify(process.env.DATABASE_URL),
STORAGE_BUCKET: JSON.stringify(process.env.STORAGE_BUCKET),
MESSAGING_SENDER_ID: JSON.stringify(process.env.MESSAGING_SENDER_ID)
}
})
],
output: {
path: __dirname,
filename: './public/bundle.js'
},
resolve: {
root: __dirname,
modulesDirectories: [
'node_modules',
'./app/components',
'./app/api'
],
alias: {
app: 'app',
applicationStyles: 'app/styles/app.scss',
actions: 'app/actions/actions.jsx',
reducers: 'app/reducers/reducers.jsx',
configureStore: 'app/store/configureStore.jsx'
},
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
},
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/
}
]
},
devtool: process.env.NODE_ENV === 'production' ? undefined : 'cheap-module-eval-source-map'
};
{
"name": "reactapp",
"version": "1.0.0",
"description": "ReactApp",
"main": "index.js",
"scripts": {
"test": "NODE_ENV=test karma start",
"build": "webpack",
"start": "npm run build && node server.js"
},
"author": "John Smith",
"license": "MIT",
"dependencies": {
"axios": "^0.16.0",
"babel-core": "^6.5.1",
"babel-loader": "^6.2.2",
"babel-preset-es2015": "^6.5.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"css-loader": "^0.23.1",
"deep-freeze-strict": "^1.1.1",
"expect": "^1.20.2",
"express": "^4.13.4",
"firebase": "^3.9.0",
"foundation-sites": "^6.3.1",
"jquery": "^2.2.1",
"moment": "^2.18.1",
"node-env-file": "^0.1.8",
"node-sass": "^4.5.2",
"react": "^0.14.7",
"react-addons-test-utils": "^0.14.6",
"react-dom": "^0.14.7",
"react-redux": "^5.0.4",
"react-router": "^2.0.0",
"redux": "^3.6.0",
"redux-mock-store": "^1.2.3",
"redux-thunk": "^2.2.0",
"sass-loader": "^6.0.3",
"script-loader": "^0.6.1",
"style-loader": "^0.13.0",
"uuid": "^3.0.1",
"webpack": "^1.12.13"
},
"devDependencies": {
"karma": "^0.13.22",
"karma-chrome-launcher": "^0.2.3",
"karma-mocha": "^0.2.2",
"karma-mocha-reporter": "^2.2.3",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.1",
"mocha": "^2.5.3"
}
}
var React = require('react');
var { connect } = require('react-redux');
import Todo from 'Todo';
var TodoApi = require('TodoApi');
export var TodoList = React.createClass ({
render: function() {
var { todos, showCompleted, searchText } = this.props;
var renderTodos = () => {
var filteredTodos = TodoApi.filterTodos(todos, showCompleted, searchText);
if(filteredTodos.length === 0) {
return (
<p className="container__message">No tasks</p>
);
}
return filteredTodos.map((todo) => {
return (
//add unique key prop to keep track of individual components
<Todo key={todo.id} {...todo} />
);
});
};
return (
<div>
{renderTodos()}
</div>
);
}
});
export default connect(
(state) => {
return state;
}
)(TodoList);
If you find an error message that you don't understand, try checking out the following resources: Heroku Error Codes (H12, R14, R15, H10, H14 etc.) - https://devcenter.heroku.com/articles/error-codes. Postgres Errors - https://devcenter.heroku.com/articles/postgres-logs-errors.
Resolution. This error message means that Heroku was unable to automatically detect the type of app you're trying to deploy: Ruby, Node, Python, PHP, Java, etc. We look for signatures for each language we support (like a pom. xml file or package.
You should not include folder node_modules in your . gitignore file (or rather you should include folder node_modules in your source deployed to Heroku). If folder node_modules: exists then npm install will use those vendored libraries and will rebuild any binary dependencies with npm rebuild .
The problem is exactly what it says. The TodoApi.
Locally it will work because the TodoApi is probably in your "node_modules" directory. (You didn't include it locally in your index.html did you?) The problem is that it is not packed into the deployment package. That's why it doesn't work.
First, don't know if you noticed but you (wan't to) include node_modules under modulesDirectories and later under module.loaders you exclude it again. Luckily for you it didn't include, otherwise you would still be building, lol. Besides that you can just delete the modulesDirectories passage. And don't ever think it's a good idea to include node_modules as a directory into anything. ;)
I would do the following to bring some structure, so you can clearly see what is included and what not. At the top of you webpack define some absolute directories:
// Absolute directories
const root = (...dir) => path.resolve(__dirname, ...dir);
const lib = root('lib'),
src = root('src'), // or 'app'
project = root('.');
This you know for sure what directory is assigned and it reads easier. Then set your entry-file.
const entry = root('app/app.jsx');
And change it under module exports:
root: src,
context: src,
entry: entry,
...
I always set the context to my src directory, that means that so far webpack is concerned src is the top directory. Remove the other stuff under entry.
I would also comment out the Uglify plugin while you don't have the build working yet, just to exclude stuff that can create error's of their own.
Install the webpack-bundle-analyzer.
npm i --save-dev webpack-bundle-analyzer
And add it to your webpack plugins like this:
new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: 'localhost',
analyzerPort: 9002,
reportFilename: 'report.html',
openAnalyzer: true,
// Log level. Can be 'info', 'warn', 'error' or 'silent'.
logLevel: 'info'
})
This will open a nice graphical view of your package after the build. One of the blocks needs to mention "TodoApi" and then you're good. If you first add the BundleAnalyzerPlugin without changing your code, then you can see that the TodoApi probably isn't in there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With