How to include only specific module in webpack without transpiling its dependencies? - node.js

I'm working with a monorepo node.js project with the following structure:
rootDir
packageA
packageB
packageC
I want to produce a bundle of packageB code using webpack and babel.js. packageA has a lot of exports however only one export is used by packageA, let's call the export utils.
When I run webpack it recognizes that packageB depends on packageA so webpack dependecnies graph now includes all modules exported by packageA in its index.js although only one is really needed. So all of the modules in packageA are being transpiled by babel.js which even includes the node_modules of packages included by files in packageA. I've run into numerous issues with transpiling node_modules with babel.js. They range from babel.js not being able to deal with non-Javascript files for which loaders do not exist, webpack not finding certain modules, having mixed esm and commonjs syntax inside some files and many others*.
Other issues could only be solved by commenting out 2 problematic modules exported by packageA.
As you can see the solution is not ideal and perhaps not sustainable for long. Is there a way to have webpack transpile packageA without transpiling its node_modules? I couldn't achieve this despite my numerous regex attempts. Below are my config files:
*I managed to solve the issue by adding modules: "commonjs" in babel config however this will disable webpack tree shaking mechanism which means that I need to use a separate babelrc for my project.
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'development',
target: 'node',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build')
},
devtool: 'source-map',
plugins: [
new webpack.IgnorePlugin(/^(hiredis|transifex)$/)
],
module: {
rules: [
{
test: /node_modules\/datauri\/index\.js$/,
loaders: ['shebang-loader', 'babel-loader']
},
{
test: /node_modules\/needle\/lib\/needle\.js$/,
loaders: ['file-loader', 'babel-loader']
},
{
test: /\.js?$/,
use: {
loader: 'babel-loader',
options: {
rootMode: 'upward'
}
},
include: [
path.resolve(__dirname, 'src'),
"/path/to/packageA",
"/path/to/packageC",
]
}
]
}
}
babel.config.js
module.exports = {
plugins: [
[
'#babel/plugin-proposal-decorators',
{
legacy: true
}
],
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-syntax-import-meta',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-json-strings',
'#babel/plugin-proposal-function-sent',
'#babel/plugin-proposal-export-namespace-from',
'#babel/plugin-proposal-numeric-separator',
'#babel/plugin-proposal-throw-expressions',
'#babel/plugin-proposal-export-default-from',
'#babel/plugin-proposal-logical-assignment-operators',
'#babel/plugin-proposal-optional-chaining',
[
'#babel/plugin-proposal-pipeline-operator',
{
proposal: 'minimal'
}
],
'#babel/plugin-proposal-nullish-coalescing-operator',
'#babel/plugin-proposal-do-expressions',
'#babel/plugin-proposal-function-bind'
],
presets: [
[
'#babel/preset-env',
{
targets: {
node: 'current'
},
corejs: 3,
useBuiltIns: 'usage',
modules: "commonjs"
}
],
'#babel/preset-typescript'
],
env: {
debug: {
sourceMaps: 'inline',
retainLines: true
}
}
}
UPDATE: I just noticed that I'm not ignoring node_modules in babel.config.js so I added:
ignore: [
/node_modules/
]
to my babel.config.js. This allowed me to stop using modules: 'commonjs' so I guess this did have some effect but I still get errors when trying to run the bundle, errors that resulted from transpiling node_modules for example this error.

Related

Webpack 5 webpackMissingModule error for node_modules package

I've been working on upgrading a repository from Webpack version 4 to 5, and I've encountered a very strange problem where Webpack will throw an error at runtime telling me that a package in node_modules could not include a specific "module" (referring to a subdirectory of that package). The module in question is #hapi/joi, and the error I'm receiving is:
Error: Cannot find module './types/alternatives'
At one point it was telling me that the error related to that package, but now it just throws the error with no package context. I've dug into the #hapi/joi package, and I can see this set of imports in the index.js:
const internals = {
alternatives: require('./types/alternatives'),
array: require('./types/array'),
boolean: require('./types/boolean'),
binary: require('./types/binary'),
date: require('./types/date'),
func: require('./types/func'),
number: require('./types/number'),
object: require('./types/object'),
string: require('./types/string'),
symbol: require('./types/symbol')
};
I've verified that the ./types/alternatives directory exists and that it has an index.js of its own. This package also works fine on Webpack 4. I tried upgrading to the standalone joi package, and it threw the same error.
I'm pretty confused at to why this error is throw at runtime. I would think this would be a build error, though maybe it has something to do with building the target for node?
This is my Webpack config. It's mostly unmodified from v4:
{
entry: {
adviceServer: "./src/server/index.ts",
},
target: "node",
bail: true,
mode: env.NODE_ENV,
output: {
path: path.resolve(root, "dist", "js"),
filename: `[name].${env.ASSET_HASH}.js`
},
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: "ts-loader",
options: {
context: path.resolve(root, "src"),
configFile: tsconfigPath,
transpileOnly: false
}
}
],
exclude: [
"/node_modules",
"/**/*.test.ts/"
]
},
{
test: /\.s?css$/,
use: ["isomorphic-style-loader", "css-loader", "sass-loader"]
}
]
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx", "scss", ".json"],
alias: {
styles: path.resolve(root, "src", "scss"),
canvas: path.resolve(root, "src", "etc", "fileMock"),
ws: path.resolve(root, "src", "etc", "fileMock")
},
plugins: [
new TsconfigPathsPlugin({
configFile: tsconfigPath
})
]
},
devtool: "eval-cheap-source-map",
plugins: [
new BundleTracker({ filename: "./webpack-stats.json" })
]
}
I had a similar issue upgrading from webpack 4 but with a different external library. In my case it was caused by two webpack#5 versions installed at the same time. Check your package-lock.json/yarn.lock and add overrides/resolutions to your package.json if there are several versions. If not — try removing your node_modules folder and installing packages again.

Unable to load sp-loader for spfx solution with webpack

my typescript file includes the following import:
import { SPComponentLoader } from '#microsoft/sp-loader';
But I get a lot of errors when building with webpack
npx webpack --config webpack.config.js
Here are some of the errors:
ERROR in
./node_modules/#microsoft/sp-loader/lib/requirejs/RequireJsLoader.js
Module not found: Error: Can't resolve './test/RequireJsMock' in
'C:\users\agaskell\source\repos\spfxBanner\node_modules#microsoft\sp-loader\lib\requirejs'
# ./node_modules/#microsoft/sp-loader/lib/requirejs/RequireJsLoader.js
258:14-45 #
./node_modules/#microsoft/sp-loader/lib/requirejs/SPRequireJsComponentLoader.js
# ./node_modules/#microsoft/sp-loader/lib/starter/SPStarter.js #
./node_modules/#microsoft/sp-loader/lib/index.js #
./Classic/client/bootHeader.ts # multi #babel/polyfill
./Classic/client/bootHeader.ts
ERROR in
./node_modules/#microsoft/sp-loader/lib/systemjs/SystemJsLoader.js
Module not found: Error: Can't resolve './test/SystemJsMock' in
'C:\users\agaskell\source\repos\spfxBanner\node_modules#microsoft\sp-loader\lib\systemjs'
I am trying to build my ts file into js for classic SharePoint sites and I normally use gulp for modern pages, but for classic I am using a separate bootloader.ts file and webpack.
Can anyone help?
Here is the webpack.config.js file:
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: "development",
entry: ['#babel/polyfill',
path.resolve(__dirname, './Classic/client/bootHeader.ts')],
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
},
{
test: /\.(s*)css$/,
use: [
// fallback to style-loader in development
process.env.NODE_ENV !== "production"
? "style-loader"
: MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
},
{
test: /\.(png|jp(e*)g|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 15000, // Convert images < 8kb to base64 strings
name: "images/[hash]-[name].[ext]"
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
resolve: {
extensions: [".tsx", ".ts", ".js"]
},
output: {
filename: "classicBundleAG.js",
path: path.resolve(__dirname, "Classic"),
libraryTarget: "umd"
}
};
I ended up using a workaround for this. I gave up on SPComponentLoader to load my bootstrap and instead installed bootstrap modules locally and then referenced them from my custom sass.
My thoughts are that gulp with yeoman normally handles the SPComponentLoader dependencies, but this time I am using a custom webpack and I did not want to deal with every missing dependency manually.

Sass-loader with Webpack, React and Babel not working

I apologize for a code-heavy post.
I'm both new to Node, React and Webpack and a first-time sass-loader user. I'm running into some problems which seem to be trivial, but aren't.
ERROR in ./app/index.js Module not found: Error: Can't resolve
'waterbottle.scss' in '/Volumes/500GB/Webs/waterbottle/app' #
./app/index.js 4:13-40
I've tried placing the .scss file in the app folder, as well as fiddling with the .scss path.
Installation:
I installed sass-loader with
npm install sass-loader node-sass webpack --save-dev
webpack.config.js looks like this
var path = require('path');
var webpack = require('webpack');
var HTMLWebpackPlugin = require('html-webpack-plugin');
var HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
template: __dirname + '/app/index.html',
filename: 'index.html',
inject: 'body'
});
module.exports = {
entry: __dirname + '/app/index.js',
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
}
],
rules: [
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
output: {
path: path.resolve(__dirname, 'build'),
filename: 'app.build.js'
},
plugins: [HTMLWebpackPluginConfig],
stats: {
colors: true
},
devtool: 'source-map',
watch: true
};
package.json includes this in devDependencies
"node-sass": "^4.5.3",
index.js contains
const scss = require("waterbottle.scss");
File structure:
My understanding is that sass-loader should automatically inject css into the dom. Many thanks in advance.
Sass loader documentation shows it like:
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}]
}]
}
};
Solved. I needed to install:
npm install style-loader --save
npm install css-loader --save
And path to .scss file:
const scss = require("../scss/waterbottle.scss");
I'm very curious why this is not mentioned on sass-loader website, or better yet, installed automatically when running
npm install sass-loader node-sass webpack --save-dev

Webpack not including all our js and jsx file, even those in the same directory

We are trying to upgrade our React.js application which uses WebPack to build. In our upgrades we are moving from Webpack 1.0 to 2.0 and I have made the "necessary" changes for the upgrade. It is building, and compiling, however, when I look at the files included, it is a very smaller scale of the files it was including before.
For instance, we have 34 files in our React Flux Actions directory. Some files have .js extension some .jsx. However, of the 34 files, only 1 is showing up in the build. What happened to the other 33. This one has .js extension but there are more .js files in that directory too.
What am I missing?
This is our main config file.
var path = require('path');
var webpack = require('webpack');
var StringReplacePlugin = require("string-replace-webpack-plugin");
var Environment = require('./js/environment');
module.exports = {
entry: [
'./js'
],
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js',
},
plugins: [
new StringReplacePlugin(),
new webpack.LoaderOptionsPlugin({
options: {
tslint: {
emitErrors: true,
failOnHint: true
}
}
})
],
resolve: {
extensions: ['*', '.js', '.jsx'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'js'),
path.join(__dirname, 'jsx')
]
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel-loader' ]
},
{
test: /js\/constants.js$/,
loader: StringReplacePlugin.replace({
replacements: [{
pattern: /localhost/g,
replacement: Environment.getBackendURL
}]
})
}]
}
};
This is our hot reload local version, I think both files are used, the one above and this one. But I am only doing "npm run build" command right now, then running "npm run local"
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./js/index'
],
output: {
path: path.join(__dirname, 'build-hot'),
filename: 'bundle.js',
publicPath: '/build/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// new webpack.NoErrorsPlugin()
new webpack.LoaderOptionsPlugin({
options: {
tslint: {
emitErrors: true,
failOnHint: true
}
}
})
],
resolve: {
extensions: ['*', '.js', '.jsx'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'js'),
path.join(__dirname, 'jsx')
]
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel-loader' ]
}]
}
};
Based on Michael's answer below, there is one I did find that is different and feels like could be the reason, but I did not see anything in Webpack2 for pre-loaders. Here is it.
preLoaders: [
{
test: /\.jsx?$/,
loader: "source-map-loader"
}
],
Webpack starts looking at your entry point(s) and only includes files that are being imported, it doesn't just include every file in your project (as described in Concepts - Entry of the official docs).
As your entry point is ./js it will start with ./js/index.js (that's how Node.js and therefore webpack handles importing Folders as Modules), so you're not including every file in that directory. And if you don't import them in ./js/index.js or in its dependencies, the files won't be included at all. Presumably you don't and that's why only this one file is being included in the bundle.
This behaviour hasn't changed from webpack 1 to webpack 2. It's rather surprising that it worked differently with webpack 1, but maybe you changed something in the migration process that you aren't aware of.

WebPack-Dev-Server error: require is not defined

Webpack itself is working fine, but the webpack-dev-server is not. Basically, webpack created 2 build files for me, a back-end bundle and a front-end bundle. So, I have a webpack-config.js for each of these. I want to develop my front-end code with webpack-dev-server, as you can see from my webpack-config file for my front-end-bundle.js below. When I run web-pack-dev server, it is able to find and build my front-end.js and index.html, but nothing renders in the console and it gives me a "Uncaught ReferenceError: require is not defined"
// var nodeExternals = require('webpack-node-externals');
var webpack = require('webpack');
module.exports = {
entry: './browser/entry.js',
output: {
path: './builds',
filename: 'frontend.js'
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"development"'
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"development"'
}
})
],
module: {
loaders: [
{
test: [/\.es6$/, /\.js$/, /\.jsx$/],
exclude: 'node_modules',
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-1']
}
},
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.html$/,
loader: 'html-loader'
},
]
},
resolve: {
extensions: ['', '.js', '.es6', '.json'],
root: '/Users/johnhenry/Desktop/GAMR/gamr/browser'
},
devServer: {
contentBase: 'builds/dev-build'
},
target: 'node',
// externals: [nodeExternals()]
}
The error is triggered by this in my front-end build (it is only in the dev server build, not in the non-dev-server webpack build):
function(module, exports) {
module.exports = require("url");
If anyone has insight into this, it would be much appreciated
Try adding:
target: 'web'
to your module block.
I had the same error and if anyone is still struggling with this, this solution also helped me:
... There are 2 ways to solve the issue:
1. (Recommended) Don't activate webpack-node-externals in your Webpack browser config, but let Webpack indeed bundle those modules
when targeting the web (this is probable what you want to do)
Have the external modules loaded in some other way in the browser, and add the appropriate importType flag to the webpack-node-externals configuration (either var for scripts or amd for AMD)
more details here: https://github.com/liady/webpack-node-externals/issues/17#issuecomment-284222729
I hit this issue when a webpack.config.js from a node app for the base of a react app.
I had the following:
target: 'web'
but still ran in to the same issue.
Removing reference to webpack-node-externals solved it, which does make sense when you think about what node-externals is actually doing.
I had below rule in my webpack.config.js
rules: [
{
test: /\.js$/,
use:['script-loader']
}
]
Removing above rule from webpack.config.js removed the error.
Hope this helps.

Resources