Node project: trying to use the same ES6 code for both the front end and back end - javascript

I have a need to share front end and back end code in a single javascript project. The front end code is transpiled using babel and the back end code is a node script (not a web app) that runs on the server.
There's currently just a single package.json file in the project.
I'm using --experimental-vm-modules to run the node server script which allows me to use ES6 syntax and import statements, however it requires the javascript files to have an .mjs extension.
The catch-22 I'm running into is that the file that is meant to be shared between the FE and BE (sharedConstants.js) only works on the back end with an .mjs extension and only works on the client side with a .js extension.
Another solution I considered was to put 'type: module' in package.json file, but then this breaks the compilation of the client side app which uses webpack, babel, react, etc.
The project is currently using node version v14.18.0 but I'm open to considering an upgrade if that in some way helps to resolve this issue.
UPDATE:
Webpack version is 4.42.0 and #babel/core version is 7.2.2
(also added contents of .babelrc, webpack.config.babel.js and webpack.config.common.js)
Contents of .babelrc:
{
"plugins": [
"#babel/proposal-class-properties",
"#babel/plugin-syntax-dynamic-import",
"#babel/transform-runtime",
["import", { "libraryName": "antd", "libraryDirectory": "lib"}, "antd"],
["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib"}, "antd-mobile"],
["babel-plugin-webpack-alias", { "config": "./webpack.config.common.js" }]
],
"presets": [
"#babel/preset-react", "#babel/preset-env"
],
"only": [
"./**/*.js",
"node_modules/jest-runtime"
],
"sourceType": "module"
}
Contents of webpack.config.babel.js:
let webpack = require('webpack');
let webpackCommon = require('./webpack.config.common.js');
module.exports = Object.assign({}, webpackCommon, {
mode: 'development',
entry: [
'./src/app-client.js',
/* The port on the development server must be entered in this section to
work properly. */
'webpack-dev-server/client?http://0.0.0.0:4000',
'webpack/hot/only-dev-server'
],
output: {
// Files are being written to disk so it's not certain at this point if
// they are being served from memory in a development environment.
// TODO: Confirm the performance impact of disk vs memory and if with the
// current configuration the files are being served from memory.
path: __dirname + '/public/',
filename: 'bundle.js'
},
devtool: 'source-map',
// dev server settings
devServer: {
hot: true,
filename: 'bundle.js',
publicPath: '/',
historyApiFallback: true,
contentBase: './public/index-static.html',
/* all requests are proxied to receive the response,
if it is bundle file, program uses script of dev server. */
proxy: {
'**': 'http://localhost:5000' // express server address
},
stats: {
// minimize console log
assets: false,
colors: true,
version: false,
hash: false,
timings: false,
chunks: false,
chunkModules: false
}
}
});
module.exports.plugins.push(new webpack.HotModuleReplacementPlugin());
Contents of webpack.config.common.js:
// Common webpack configuration.
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const fs = require('fs');
const lessToJs = require('less-vars-to-js');
const antdThemeVarsOverride = lessToJs(
fs.readFileSync(
path.join(__dirname, './src/static/less/antd-theme-vars-override.less'),
'utf8'
)
);
const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: ['./src/app-client.js', './src/static/less/main.less'],
output: {
path: __dirname + '/public/',
filename: 'bundle.js'
},
module: {
// NOTE! If you change the order of the rules, the end of the prod
// webpack config file must be changed as well!
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{loader: 'react-hot-loader/webpack'},
{
loader: 'babel-loader',
options: JSON.stringify({
babelrc: false,
cacheDirectory: true,
presets: ['#babel/preset-env', '#babel/react'],
plugins: [
'#babel/transform-runtime',
'#babel/proposal-class-properties',
'lodash',
['import', {libraryName: 'antd', style: true}, 'antd'],
[
'import',
{libraryName: 'antd-mobile', style: true},
'antd-mobile'
]
]
})
}
]
},
{
test: /\.modernizrrc.js$/,
use: ['modernizr-loader']
},
{
test: /\.modernizrrc(\.json)?$/,
use: ['modernizr-loader', 'json-loader']
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.less$/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: 'css-loader'},
{
loader: 'less-loader',
options: {
modifyVars: antdThemeVarsOverride,
javascriptEnabled: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
},
resolve: {
modules: ['node_modules', path.join(__dirname, 'src')],
alias: {
Actions: path.resolve(__dirname, 'src/actions/'),
Components: path.resolve(__dirname, 'src/components/'),
Containers: path.resolve(__dirname, 'src/containers/'),
Db: path.resolve(__dirname, 'server/db/'),
Table: path.resolve(__dirname, 'src/components/HipocampoTable/'),
Js: path.resolve(__dirname, 'src/js/'),
Layouts: path.resolve(__dirname, 'src/components/layouts/'),
Reducers: path.resolve(__dirname, 'src/reducers/'),
Selectors: path.resolve(__dirname, 'src/selectors/'),
Server: path.resolve(__dirname, 'server/'),
Scripts: path.resolve(__dirname, 'scripts/'),
Spreadsheets: path.resolve(__dirname, 'spreadsheets/'),
Shared: path.resolve(__dirname, 'src/shared/'),
Slices: path.resolve(__dirname, 'src/slices/'),
Static: path.resolve(__dirname, 'src/static/'),
Support: path.resolve(__dirname, 'cypress/support/'),
modernizr$: path.resolve(__dirname, '.modernizrrc')
},
extensions: ['.js', '.json', '.less', '.jsx', '.css', '.sql']
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{from: './static/favicon.png', to: 'favicon.png'},
{from: './static/circle_logo.png', to: 'circle_logo.png'}
]
}),
new HTMLWebpackPlugin({
title: 'Hipocampo',
template: './static/index-template.html',
filename: 'index-static.html',
// This is needed to ensure that the password reset link works.
publicPath: '/',
minify: {
collapseWhitespace: false,
removeComments: false
}
}),
// This is to reduce the moment.js webpack size
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en-US/),
new MiniCssExtractPlugin()
]
};

Related

Configure JSdoc wtih Webpack

I started my Webpack configuration last week, and a problem appear : How can I run a jsdoc generation with Webpack ?
All Jsdoc Webpack seems to be deprecated or out-to-date...
My simple configuration file :
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require('eslint-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
let config = {
entry: {
app: ['./src/js/css.js', './src/js/app.js']
},
output: {
filename: 'assets/js/[name].[contenthash].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
mode: 'development',
// mode: 'production',
devtool: 'inline-source-map',
module: {
rules: [
// LOADER BABEL
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
targets: ['defaults'],
plugins: ['#babel/plugin-proposal-object-rest-spread']
}
}
},
// LOADER STYLE + CSS
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html"
}),
new ESLintPlugin({
extensions: ["js", "jsx", "ts", "tsx"],
})
]
};
let analyzerMode = process.argv.find(param => param === '--analyze');
config.plugins.push(new BundleAnalyzerPlugin({
openAnalyzer: false,
defaultSizes: 'gzip',
analyzerMode: analyzerMode ? 'static' : 'disabled'
}));
module.exports = config;
Now, on my project, it's possible to run jsdoc src/js/, it will be generate out folder with all JS Documentation.
How can I automate this process command with webpack.config.js ?
You can append the command to the build script in package.json
{
"name": "your_project_name",
...
"scripts": {
"build": "webpack --mode production && jsdoc src/js/"
}
...
}
If you just want '.d.ts' files you can use npx tsc. It will do the work if tsconfig.json is properly configured like in the link below
https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html

React native web storybook react-native-vector-icons problem icon

I'm developing a react native component on storybook, which uses react-native-paper and react-native-vector-icons.
The problem is that I can't see the icons, I tried to follow the guide on react-native-vector-icons, this: webpack
Below is the webpack, but I didn't quite understand how to use the second part of the code suggested in the guide, where and how I should use it.
Can anyone help me out?
webpack:
const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
template: path.resolve(__dirname, './public/index.html'),
filename: 'index.html',
inject: 'body',
})
module.exports = {
entry: path.join(__dirname, 'index.web.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, '/build'),
},
resolve: {
alias: {
'react-native$': 'react-native-web',
'#storybook/react-native': '#storybook/react',
'styled-components/native': 'styled-components',
},
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules[/\\](?!react-native-vector-icons)/,
use: {
loader: 'babel-loader',
options: {
// Disable reading babel configuration
babelrc: false,
configFile: false,
presets: [
'#babel/preset-env',
'#babel/preset-react',
'#babel/preset-flow',
'#babel/preset-typescript',
{
plugins: ['#babel/plugin-proposal-class-properties', '#babel/plugin-proposal-object-rest-spread'],
},
],
},
},
},
{
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
loader: 'file-loader',
},
{
test: /\.ttf$/,
loader: 'url-loader', // or directly file-loader
include: path.resolve(__dirname, 'node_modules/react-native-vector-icons'),
},
],
},
plugins: [HTMLWebpackPluginConfig],
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true,
},
}
The reason for your problem could be that your webpack.config.js is located in the folder .storybook.
So you have to change the path for loading the react-native-vector-icons and add ../ before node_modules, because of the folder structure.
...
{
test: /\.ttf$/,
loader: 'url-loader', // or directly file-loader
// add .. to the path for node_modules
include: path.resolve(__dirname, '../node_modules/react-native-vector-icons'),
},
...
An similar issue has been described and solved here: React Native Vector Icons don't load on react-native-web storybook

Uncaught ReferenceError: require is not defined - Webpack2

I'm updating an app to use webpack 2 from webpack 1 and the regular build works fine. The issue seems to arise when using the devServer and requiring just one of the chunks generated (it's an electron app so I have main and renderer chunks - both are included in a regular build, and with the dev server only the renderer chunk is included)
Everything worked on webpack 1, but for some reason the runtime isn't being included in my chunks? I've tried reordering them but to no avail.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = {
context: __dirname,
entry: {
main: './main.js',
renderer: './app/index.jsx'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: "[name].js"
},
node: {
__dirname: false,
__filename: false
},
devtool: 'cheap-eval-source-map',
target: 'electron',
module: {
loaders: [
{
test: /(\.js$|\.jsx$)/,
exclude: /(node_modules|dist)/,
loader: 'babel'
},
{ test: /\.scss$/, loader: "style!css?modules!sass" },
{ test: /\.png$/, loader: "url?limit=100000" },
{ test: /\.jpg$/, loader: "file" }
]
},
resolve: {
extensions: ['.js', '.jsx', '.scss'],
modules: [path.resolve('./app'), 'node_modules']
},
plugins: [
new HtmlWebpackPlugin({
template: './app/index.html',
chunks: ['renderer'],
inject: 'body',
hash: 'true'
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"dev"'
}
})
],
devServer: {
contentBase: __dirname
},
externals: {
'cheerio': 'window',
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
}
};
It seems one of the chunks isn't included properly:
/***/ },
/* 26 */
/***/ function(module, exports) {
module.exports = require("url");
Why is this?
So it seems the issue was with me setting the target to electron but building for web. This seemed to work in webpack 1, but after updating to webpack 2 it no longer includes the runtime in both bundles.
The solution I took was to have two configs - one for electron (my main build), and one for web (specify the target in the webpack config)

webpackJsonp is not defined: webpack-dev-server and CommonsChunkPlugin

This is my webpack.config.js file:
const webpack = require('webpack');
const path = require('path');
module.exports = {
cache: true,
devtool: 'source-map',
entry: {
app : [
'./src/index.js'
],
vendor: ['lodash']
},
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
publicPath: '/dist/',
pathinfo: true
},
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loaders: ['babel'] },
{ test: /\.scss/, exclude: /node_modules/, loaders: ['style', 'css', 'sass'] }
]
},
plugins: [
new webpack.NoErrorsPlugin(),
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js', Infinity)
]
};
And this is my scripts that runs the webpack-dev-server:
const webpack =require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('../webpack.config');
const _ = require('lodash');
const webpackDevConfig = _.cloneDeep(webpackConfig);
const devPort = 3000;
webpackDevConfig.entry.app.unshift('webpack/hot/dev-server');
webpackDevConfig.entry.app.unshift('webpack-dev-server/client?http://localhost:' + devPort + '/');
webpackDevConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
new WebpackDevServer(webpack(webpackDevConfig), {
publicPath: webpackConfig.output.publicPath,
hot: true,
stats: {
colors: true,
chunks: false
}
}).listen(devPort, 'localhost');
The webpack command output is good (bundle.js and vendor.bundle.js), however, the dev server fails with webpackJsonp is not defined (although its in-memory compilation succeeded).
When removing CommonsChunkPlugin from webpack.config.js - it all works fine:
...
entry: [
'./src/index.js'
],
...
plugins: [
new webpack.NoErrorsPlugin()
]
Any ideas?
In your index.html file just call vendor.bundle.js before bundle.js
<script src="assets/js/vendor.bundle.js"></script>
<script src="assets/js/bundle.js"></script>
That's all, now it should work. More information.
Rename vendor entry point to
'vendor.js': ['lodash']
Just to expand a little on the concept, the vendor has to come first since the runtime is contained in there (everything that defines all the variables and methods run during client load time because of all the webpacking).
If you use a manifest file (because of chunking and so on), you'll have to put that first since it will then contain the runtime because of the way the module is built.

Make Webpack render in a file other than an index

By default Webpack looks for a specific index.html file in a specified directory, right? What I want to know is can I tell webpack to look for and inject my bundled files in a file that I specify?
I ask this because I'm trying to develop a Ghost theme with webpack and by default, a Ghost theme looks for a default.hbs file to serve as an index file.
I tried to use the HtmlWebpackPlugin to set the filename for entry all while making the same file for web pack's output, but it's not working.
This is my current webpack.dev.config.js:
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:2368',
'webpack/hot/only-dev-server',
'./src/router'
],
devtool: 'eval',
debug: true,
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js',
publicPath: '/static/'
},
resolveLoader: {
modulesDirectories: ['node_modules']
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
hash: true,
filename: 'default.hbs',
template: __dirname + '/public/default.hbs',
})
],
resolve: {
extensions: ['', '.js', '.sass', '.css', '.hbs']
},
module: {
loaders: [
// js
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel'],
include: path.join(__dirname, 'src')
},
// CSS
{
test: /\.sass$/,
include: path.join(__dirname, 'src'),
loader: 'style-loader!css-loader!sass-loader'
},
// handlebars
{
test: /\.hbs$/,
include: path.join(__dirname, 'public'),
loader: 'handlebars-template-loader'
}
]
},
node: {
fs: 'empty'
}
};
Easiest way: you can configure webpack to use any file and template file via the html-webpack-plugin plugin. You can also specify the element to inject into.
import HtmlWebpackPlugin from 'html-webpack-plugin';
[...]
const plugins = [
new HtmlWebpackPlugin({
template: 'templates/myTemplateFile.tpl.html',
inject: 'body',
filename: 'myOutputFile.html'
})
];

Categories

Resources