include a config file in webpack bundle - node.js

In my webpack project I want to bundle my project and include an example usage file with it. However when I bundle my project in this form
module.exports = {
entry: {
main: './src/main.js',
about: './src/pages/config.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: 'report' //makes this a global variable we can call from basic javascript page,
devtool: 'source-map',
devServer: {
port: 3001,
clientLogLevel: 'none',
stats: 'errors-only'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] //npm install --save-dev style-loader css-loader
}
]
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'ESS Map Learning',
}),
],
};
},
The outputted file in the dist is converted or loaded into bundled code and is unreadable as an example. I am trying to find a way to load this file so it is not changed and sits in the bundle in its original format

If you want to include an example usage file along with your bundle, you can try the following two ways:
1. use CopyWebpackPlugin
With this plugin, you can copy specific files from your source project into your output folder.
First you need to install the plugin:
npm install --save-dev copy-webpack-plugin
Then in your webpack configuration file:
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin([
{
from: "path/to/some-example.js",
to: "" // leave blank if you want to keep the folder hierachy as in the source code, otherwise specify an output path
},
]),
]
}
More about the plugin:
Webpack Plugins: CopyWebpackPlugin
2. use the "Asset Modules" configuration
module.exports = {
module: {
rules: [
{
test: /example\.js/, // regex to match your source example file path
type: 'asset/resource', // files with this type will be copied to your output folder
generator: {
filename: 'examples/[name][ext]', // you can give a desired name for your output file, or webpack will defaultly name it with hash value
}
}
]
},
// rest of your webpack configuration
// ...
}
Caveats: I think webpack will never process your example file if it is not a dependency of any of your entry points (which is very likely the case). In this sense I guess using the CopyWebpackPlugin is more reliable.
More about asset modules:
Webpack Guides: Asset Management
Webpack Guides: Asset Modules

Related

How to include only specific module in webpack without transpiling its dependencies?

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.

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.

WebPack 4 SplitChunksPlugin for NodeJS

I'm producing bundles for a NodeJS 8.x (AWS Lambda) and would like to selectively minify chunks (business code to remain non-minified and third party libraries to be minified to save on bundle size).
I've configured Webpack as below but it main bundle of mine doesn't work because it is not built to be able to require the other files (one file for each npm dependency).
How can I configure WebPack to produce chunks which can require each other in NodeJS?
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: (module) => {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName}`;
},
},
},
},
Instead of minifying the output chunks selectively, I opted to not use the SplitChunksPlugin at all and instead minify in a custom loader as part of the compilation.
This makes it possible to cherry pick exactly which input files (Webpack calls them assets) you want to minify and which ones you don't.
npm install terser --save-dev
tools/es-minify-loader.js
const Terser = require('terser');
module.exports = function esMinifyLoader(source) {
const output = Terser.minify(source, { compress: true, mangle: true });
return output.code;
};
webpack.config.js
...
module: {
rules: [
{
exclude: [
/dont-minify-this-file\.js/,
/some-library-or-folder-you-dont-want-minified/i,
],
test: [
/\.js$/
],
use: [
{
loader: path.resolve(__dirname, 'tools/es-minify-loader.js'),
}
]
}
]
},
...

Using paths in TypeScript in a NodeJS project

I'm trying to use the paths property in tsconfig.json in a NodeJS project.
In tsconfig.json I have something like this:
"baseUrl": ".",
"paths": {
"#myApp/server/*": [
"server/src/*"
],
"#myApp/common/*": [
"common/src/*"
]
},
Running tsc outputs all JS files as expected, but they retain the #myApp... imports. As a result, node won't run as it can't resolve all modules having as path #myApp....
I can't find a way to convert the paths I've set in tsconfig.json to a value that can be used by node. I've only found this question on SO, but it's quite outdated and it does not lead to a clean solution.
Do we have a way to transpile TS to JS in a way in which we are able to use paths?
If anyone out here is using with nodejs and tsconfig-paths. You can use the following commands to make the absolute paths work:
// With ts-node
ts-node -r tsconfig-paths/register src/main.js
// With node
node -r ts-node/register/transpile-only -r tsconfig-paths/register dist/main.js
Got this from here:
https://github.com/dividab/tsconfig-paths/issues/61
Until today I was using tspath as suggested in the accepted answer, but I started to get some issues with relative paths having as baseUrl: '../'.
I switched to a webpack based approach, with this webpack.config.ts config:
import { TsConfigPathsPlugin } from 'awesome-typescript-loader';
import * as fs from 'fs-extra';
import { join } from 'path';
import * as webpack from 'webpack';
const packageConfig = fs.readJSONSync('./package.json', { encoding: 'utf-8' });
const externals = {};
for (const packageName in packageConfig.dependencies)
externals[packageName] = packageName;
const serverConfig: webpack.Configuration = {
entry: {
index: './src/index.ts'
},
resolve: {
extensions: ['.ts', '.js'],
plugins: [
new TsConfigPathsPlugin({ configFileName: 'tsconfig.json' })
]
},
target: 'node',
node: {
__dirname: false
},
externals,
output: {
path: join(__dirname, 'dist'),
filename: '[name].js',
library: '[name]',
libraryTarget: 'umd'
},
module: {
rules: [{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
}]
},
plugins: [
new webpack.BannerPlugin(`Copyright © 2018-${new Date().getFullYear()} BOHR. All rights reserved.`)
],
mode: 'production',
optimization: {
minimize: false
}
};
// tslint:disable-next-line:no-default-export
export default [serverConfig];
Note the imports, you will need to add few packages to use this configuration.
I've found this npm package that converts all absolute paths to relative paths: https://www.npmjs.com/package/tspath.
Running tsc will produce the files with the absolute paths (e.g. #myApp/server/my-library). Then run tspath will convert all paths to the relative path.
Why won't you just use the path module in the code and resolve the path to rest of the directories relative to the one where you started the server from?
Take a look at the line 6 in this example:
https://github.com/Farata/angulartypescript/blob/master/code-samples/Angular6/chapter12/server/rest-server-angular.ts.
The __dirname points at the directory where the server was started from, and public is a subdirectory of this directory. I didn't use the paths option of tsc.

Webpack bundling custom Utilities separately from App code

I would like to bundle up my TypeScript ReactJs project using webpack. As my project is rather large, I want to bundle the utils separately from the main App code, so I can keep them separate.
I have the following folder structure;
Scripts
- App
- Components
- ComponentOne.tsx
- App.tsx
- Utilities
- Interfaces
- IHelperInterface.ts
- Interfaces.ts
ComponentOne imports IHelperInterface with a an import statement import { IHelperInterface } from '../../Utilities/Interfaces/IHelperInterface';
Along with my custom Utils, I am also using npm for various packages.
So my current webpack config looks like this;
var webpack = require('webpack'),
pkg = require('./package.json');
module.exports = {
entry: {
app: './scripts/app/app',
vendor: Object.keys(pkg.dependencies)
},
output: {
filename: '[name].bundle.js',
path: __dirname + '/wwwroot/js/app'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' })
],
// 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: 'awesome-typescript-loader' },
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' }
]
}
};
My vendor files (npm packages) are being bundled together, and currently the rest is then bundled into 1 file. How can I modify the config to bundle my Utils?
I tried adding a 2nd entry point;
entry: {
app: './scripts/app/app',
utils: './scripts/interfaces',
vendor: Object.keys(pkg.dependencies)
},
In the hope that this would work, however it created utils.bundle.js file, but the app.bundle.js file still had all the code in it.

Resources