I have a legacy code for my express app that read all routes files in specific dir and require them in a loop. Notice this code cant be changed:
app.js
const normalizedRoutes = fs.readdirSync(__dirname + '/src/routes/')
.map(routeFile => `/src/routes/${routeFile}`);
normalizedRoutes.forEach((normalizedRouteDir: string) => {
require(normalizedRouteDir)(app);
})
Now, I want to combine a Server Side Rendered application with the code above, using some JSX in routes files.
My problem is because the routes files are loaded on run time webpack not recognize them when creating the bundle.js file.
Therefore there are not routes files in the /src/routes/${routeFile} and when I run the bundle.js file I get an error message of:
Error: ENOENT: no such file or directory, scandir '/Users/******/build/src/routes/'
(the stars are for hiding full path)
webpack configs:
webpack.base.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: { //remain
rules: [
{
test: /\.(ts|js)x?$/,
loader:'babel-loader',
exclude: /node_modules/,
options:{
presets:[
'#babel/react',
['#babel/env',{targets:{browsers:['last 2 versions']}}]
]
}
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
}
};
webpack.server.js
const path = require('path')
const {merge} = require('webpack-merge')
const baseConfig = require('./webpack.base.js');
const webpackNodeexternals = require('webpack-node-externals');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = {
mode: "development",
entry: {
main:"./app.ts",
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts"],
},
node: {
__dirname: true
},
output: {
libraryTarget: "commonjs",
path: path.join(__dirname, "build"),
filename: "bundle.js",
},
target: "node",
//Avoid put node modules of server when sending to browser
externals: [webpackNodeexternals()]
}
module.exports = merge(baseConfig,config)
scripts from package.json:
"dev:server": "nodemon --watch build --exec \"node build/bundle.js\" ",
"dev:build-server": "webpack --config webpack.server.js --watch",
When I copy the route files (js files) to the build directory it works of course but that means I don't run webpack on these files and therefore I can't include JSX\es6 features inside these files.
So my question is:
Is there any possible way to make these requires identify by webpack/babel to add them to bundle.js and avoid the need for seperate files (bundle.js and routes files)
If we cant do it, how can I run webpack on a folder seperatly from the bundle.js output and create a route folder in the correct path but after processed by babel?
Thanks!
Instead of using a Webpack you can try using a programmatic interface of babel, and transpile the files before requiring them.
Here is the link https://babeljs.io/docs/en/babel-core
Related
I've tried almost every solution but I can't find any solution for this problem. I'm using express to render my ReactJS code, build with webpack. I can open pages without any issues until I'm being redirected from home page. But when I tried entering the URL on browser or refresh the page, I cannot see the page. Instead I see this error:
Cannot GET /path
I have also tried adding historyApiFallback: true to my webpack.config.js but no luck there.
Below are my scripts from package.json
{
...
"scripts": {
"build": "webpack --config webpack.config.js",
"dev": "webpack-dev-server --mode development --open",
"start": "npm run build && node server.js"
},
...
}
And this is my webpack.config.js:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require('path');
const htmlPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
});
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
publicPath: '/',
},
devServer: {
historyApiFallback: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
]
},
plugins: [htmlPlugin],
resolve: {
extensions: ['*', '.js', '.jsx']
},
};
And the server.js file:
const path = require('path');
const express = require('express');
const PORT = process.env.PORT || 3000;
const app = express();
app.use(express.static(path.join(__dirname, 'dist')));
app.get('/', function(request, response) {
response.sendFile(__dirname + '/dist/index.html');
});
app.listen(PORT, error => (
error
? console.error(error)
: console.info(`Listening on port ${PORT}. Visit http://localhost:${PORT}/ in your browser.`)
));
Important NOTE
When I run server with npm run dev, there is no issues. I can enter URL manually and refresh the page.
But when I run server with npm run start, I am facing the issue I described above.
app.get('/', ... ); only sends back the index.html on the / path, you need to return it on every path
app.get('*', function(request, response) {
response.sendFile(__dirname + '/dist/index.html');
});
I made a webapp with angular. I want server side rendering with Angular Universal.
The best way to build such a server is by using webpack, this is the standard configuration which works for most of the cases:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: { server: './server.ts' },
resolve: { extensions: ['.js', '.ts'] },
target: 'node',
externals: [/node_modules/],
mode: 'none',
// this makes sure we include node_modules and other 3rd party libraries
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [{ test: /\.ts$/, loader: 'ts-loader', exclude: /server-local/ }]
},
plugins: [
// Temporary Fix for issue: https://github.com/angular/angular/issues/11580
// for 'WARNING Critical dependency: the request of a dependency is an expression'
new webpack.ContextReplacementPlugin(
/(.+)?angular(\\|\/)core(.+)?/,
path.join(__dirname, 'src'), // location of your src
{} // a map of your routes
),
new webpack.ContextReplacementPlugin(
/(.+)?express(\\|\/)(.+)?/,
path.join(__dirname, 'src'),
{}
)
]
};
But I got a problem: I use the node module pug-mailer in my server and it uses uglify-js as dependency and not dev dependencies. Webpack has no support in bundling uglify-js, so the only way I found to solve this problem was this:
const nodeExternals = require('webpack-node-externals');
module.exports = {
externals: [nodeExternals()],
... //the rest is unchanged
};
But it cannot be accepted: with this, all node_modules are not bundled and I have to keep the entire node_modules dir in my production server.
Is there a way to bundle all the node_modules in only one file except for uglify-js?
The result I want is this:
dist
----server.js //built with webpack, contains all node_modules except uglify-js
----other dists folder
node_modules
----uglify-js //Only this dependency in node_modules
I'm trying to update my app node backend to typescript and I keep hitting this syntax error:
/Users/Shared/website/src/server.ts:1
(function (exports, require, module, __filename, __dirname) { import *
as express from 'express';
^
SyntaxError: Unexpected token *
I have my tsconfig/webpack/server, etc set up as follows:
server.ts
import * as express from 'express';
import * as path from 'path';
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname, 'dist')));
app.get('*', function(req, res, next){
res.sendFile(path.resolve(__dirname, '/public', 'index.html'));
});
app.listen(port, () => console.log(`Listening on port ${port}!`));
webpack.config.json:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: "./src/index.tsx",
output: {
filename: "bundle.js",
path: path.join(__dirname, outputDirectory)
},
mode: 'development',
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".json"]
},
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: "ts-loader" },
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
{
test: /\.scss$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
}
]
},
// When importing a module whose path matches one of the following, just
// assume a corresponding global variable exists and use that instead.
// This is important because it allows us to avoid bundling all of our
// dependencies, which allows browsers to cache those libraries between builds.
externals: {
},
plugins: [
new CleanWebpackPlugin([outputDirectory]),
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.gif'
}),
new CopyWebpackPlugin([
{ from: 'public' }
])
]
};
tsconfig.json:
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "commonjs",
"target": "es6",
"jsx": "react"
},
"include": [
"./src/**/*"
],
}
The build process succeeds, but I hit the syntaxError on run. I have a react front end also set up using typescript / tsx and it works just fine. I seem to just be running into issues with the server.ts / node files. I'm very new to trying to get this all set up, but I wanted practice making a site with numerous technologies (react/node/typescript/sass/webpack/etc). So far I have everything except the typescript/node relationship.
I had the same problem, after I realised that the tsconfig wasn't applied.
If
"module": "commonjs"
was in effect, you wouldn't have this error.
I faced the same problem before. I solve it by changing the import 'call':
From:
import * as express from 'express';
To:
const express = require('express');
As others have stated, the problem is that your tsconfig.json is not applied to server.ts. You have left out important details of your setup which is why others cannot duplicate this problem.
I have a good guess at what the issue is and having struggled with this identical problem myself, I will explain my guess here in the hope of helping some other poor soul being tormented by this issue.
The basic problem is that your server code is in a different tree than your react code. This is why the tsconfig.json is not being applied to it since (I believe) it is outside the "./src/" path specified. (Perhaps "./website/src/").
You haven't shown us the package.json file but very likely the server entry is something like "server": "ts-node ./website/src/server.ts"
To verify that the tsconfig.json application is the issue try this from the command line...
$ ts-node -O '{\"module\":\"commonjs\"}' ./website/src/server.ts
Chances are, things will start working. Now the solution is probably as simple as adding another path your tsconfig.json includes.
So I came across this post on github where basically there are two sort of working methods presented since you’re using a bundling tool targeted to es6 add
"allowSyntheticDefaultImports": true to ”compilerOptions”
Or change
import express from "express";
to
import express = require('express');
This might be happening if your tsconfig.json isn't at the root of your project. Configure webpack to point to your target config file (which you can alter with variables to point to dev and prod configurations).
https://github.com/TypeStrong/ts-loader
If set, will parse the TypeScript configuration file with given absolute path as base path. Per default the directory of the configuration file is used as base path. Relative paths in the configuration file are resolved with respect to the base path when parsed. Option context allows to set option configFile to a path other than the project root (e.g. a NPM package), while the base path for ts-loader can remain the project root.
Try modifying your webpack.config.js file to have this:
module.exports = {
...
module: {
rules: [
{
options: {
configFile: path.join(__dirname, "tsconfig.json")
}
}
]
}
};
I am developing a serverless application using the Serverless Framework. I need webpack to compile the .js files inside root folder as well as 'src' folder. The config file and 'src' folder are right inside root folder. This is the webpack.config.js file
var glob = require('glob');
var path = require('path');
var nodeExternals = require('webpack-node-externals');
// Required for Create React App Babel transform
process.env.NODE_ENV = 'production';
module.exports = {
// Use all js files in project root (except
// the webpack config) as an entry
entry: globEntries('!(webpack.config).js'),
target: 'node',
// Since 'aws-sdk' is not compatible with webpack,
// we exclude all node dependencies
externals: [nodeExternals()],
// Run babel on all .js files and skip those in node_modules
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
include: __dirname,
exclude: /node_modules/,
}]
},
// We are going to create multiple APIs in this guide, and we are
// going to create a js file to for each, we need this output block
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js'
},
};
function globEntries(globPath) {
var files = glob.sync(globPath);
var entries = {};
for (var i = 0; i < files.length; i++) {
var entry = files[i];
entries[path.basename(entry, path.extname(entry))] = './' + entry;
}
return entries;
}
I am trying to run webpack from within nodejs. My directory structure looks like this:
build
|- dev.js
dist
|- bundle.js
src
|- layout
|- App.js
|- server
|- app.js
dev.js:
const webpack = require('webpack');
const path = require('path');
// returns a Compiler instance
const compiler = webpack(
{
context: path.resolve(__dirname, '../src'),
entry: [
'./server/app.js'
],
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
}
]
}
}
);
compiler.run(function(err, stats) {
if(err) {
console.log('Err');
}
});
app.js:
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from '../layout/App';
const app = express();
app.get('*', (req, res) => {
res.send(renderToString(<App />));
});
app.listen(3000, () => {
});
If I now run node build/dev.js it generates my bundle.js, but it does not find my entry module.
(function webpackMissingModule() { throw new Error("Cannot find module \"./server/app.js\""); }());
Even though I think this is how the entry point should be defined, I have tried many combinations, even the absolute path. But always the same result, it cannot find the entry module. What am I missing here?
It looks like something is going wrong with bundling the assets - since webpack looks in your node_modules to find its assets it, would be best if you could post your package.json alongside the other files.
You're using webpack programatically instead of the default webpack command, which normally gives a lot of debug output (along with information about the build-steps, errors, etc.) - I would suggest using that to debug it, and then switch to the programmatical approach when you know that everything is working.
It looks like you want to do server-side rendering with react. Some special configuration is needed for your webpack configuration then. This is already handled here: http://jlongster.com/Backend-Apps-with-Webpack--Part-I
Without having the package.json it is really hard to debug exactly what goes wrong here.
Assuming your package.json is correct, here's a dev.js file that would work with the above code:
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
// returns a Compiler instance
const compiler = webpack(
{
context: path.resolve(__dirname, "../src"),
target: 'node',
entry: [
'./server/app.js'
],
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
}
]
},
externals: nodeModules
}
);
compiler.run(function(err, stats) {
console.log(err, stats);
});