Override some Webpack 5 NodeJS modules with polyfill - javascript

I have NodeJS code that I now need to move to an embedded system. It takes far too long to simply start NodeJS ("Hello World" ~11sec on a BeagleBone Black) so we needed an alternative. The IoT.js looks promising but it does not support some of the internal NodeJS modules (e.g. url, zlib, tty)--which my code needs. I am using Webpack 5.35.0 to create a single file for my code but this is where my problems lie. I want to use Webpack with a node target since IoT.js offers most of what node offers natively. However is there a way to force Webpack to use polyfills for some of the modules? For example, browserify-zlib instead of expecting nodes zlib.
My basic Webpack configuration is simple:
{ target: 'node10.17',
entry: './index.js',
output:
{ filename: 'index.js',
path: '/work/proj/dist',
libraryTarget: 'umd' },
stats: 'errors-only',
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
}
}
I have done some reading where people claim adding a simple resolve.fallback.zlib = false and resolve.alias should do the trick--which is not working for me.
I tried to simply add resolve.fallback.zlib = false in the hopes to just have zlib omitted from the Webpacked output and this did not work. No matter what I do the standard Webpack boilerplate "node" zlib include code exists.
Standard Webpack boilerplate when using node target.
/***/ "zlib":
/*!***********************!*\
!*** external "zlib" ***!
\***********************/
/***/ ((module) => {
"use strict";
module.exports = require("zlib");;
/***/ })
Other things I tried were--ALL of which did not work:
I was hoping this would alias zlib and actually put in the browserify-zlib code.
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
alias: { zlib: '/work/proj/node_modules/browserify-zlib/lib/index.js' },
fallback: {} } }
Same as the previous example but thought by disabling the fallback the alias/polyfill would go into the output. This is what others online had success with.
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
alias: { zlib: '/work/proj/node_modules/browserify-zlib/lib/index.js' },
fallback: { zlib: false } } }
Here I just hoped to not include zlib to see if Webpack would omit it with a node target.
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
fallback: { zlib: false } } }
Lastly I tried to use the plugin node-polyfill-webpack-plugin but with the node target it does not seem to do anything. If I chose a web target the plugin seems to work as I'd expect (taken from here). Again, I'd prefer a node target so it uses native modules and the setup seems cleaner; but maybe this is the only approach. If this is the approach then how to support fs and other non-browser modules that IoT.js supports natively?
...
plugins = [ new NodePolyfillPlugin({ excludeAliases: [] }) ];
It seems that when the node target is selected there is no way to override any of the default/boilerplate code added to the output file. Does anyone have experience with IoT.js and Webpack, or overriding the default Webpack 5 code for node and use a polyfill instead? Not sure if a Webpack plugin is an approach. I am a little new to Webpack. Could this be a problem with Webpack? Any help would be appreciated.

Related

Rollup UMD Module Reference to Named Imports is Undefined

I had a published component library (my-components) that had a specific component (Compare) in it, along w/ other components and utilities that were used in a main application. We use rollup and create several different output formats, one of which is UMD (required by one of the teams using my-components).
The component (Compare) was starting to become large, so it was moved out on its own. The rollup build was based off of what was used for my-components. After moving it out to its own repository to be built as its own component, it now had a dependency on my-components. When used in the main application (CRA app), everything works as expected. However, when the team who uses the UMD module tried to use the Compare app, they started to get errors:
Cannot read properties of undefined (reading someService)
The code in Compare is:
import {someService, somethingElse} from 'my-components';
. . .
someService.doSomething();
I saw the following warning during the build:
WARNING: { code: 'MISSING_GLOBAL_NAME', guess: 'myComponents', message: 'No name was provided for external module \'my-components\' in output.globals - guessing \'myComponents\'' }
So I explicitly added a global entry for it.
When I looked at the resulting code in the UMD module, the code looked like:
myComponents.someService.doSomething();
Why doesn't myComponents.someService resolve to the class that is exported from my-components?
My rollup.config.js looks like:
const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.json'];
const umdGlobals = {
axios: 'axios',
lodash: '_',
react: 'React',
'react-dom': 'ReactDOM',
'my-components': 'myComponents'
};
export default [
{
input: 'src/components/index.js',
output: [
{ file: pkg.module, format: 'esm', sourcemap: true },
{ file: pkg.main, format: 'cjs', sourcemap: true },
{ file: pkg.umd, name: 'Compare', format: 'umd', sourcemap: true, globals: umdGlobals }
],
plugins: [
autoExternal(),
resolve({ extensions: EXTENSIONS, preferBuiltins: false, browser: true}),
commonjs({include: ['node_modules/**']),
babel({
babelHelpers: 'bundled',
babelrc: false,
presets: ['#babel/preset-env', '#babel/preset-react', '#babel/preset-typescript'],
plugins: ['#babel/plugin-transform-arrow-functions', '#babel/plugin-proposal-object-rest-spread', '#babel/plugin-proposal-class-properties],
extensions: EXTENSIONS,
exclude: 'node_modules/**'
}),
json(),
requireContext(),
internal(['classnames', 'pluralize'])
]
}
];
I tried to put my-components inside of internal(), and remove it from the global list, but that started to give other warnings that I think were the result of a dependency inside of it, and would throw errors when the bundle was used anyway.
Any advice on what I may be missing? Is there something wrong w/ the output from the my-components that I should be trying to fix in its rollup config, or is there something I can do in my rollup config for Compare?
Any help is greatly appreciated.

webpack babel how to copy transpiled file if i don't use import/require

I have a file in my project called test.js
I don't import/require it anywhere which means my webpack won't call babel-loader for that js file.
Question: what I want is to move test.js into /dist folder, but as a compiled/transpiled. What's the best practice for it?
What I tried: I tried to use a copy-webpack-plugin and use its transform parameters before copying the file, but I can't seem to find the good babel package.
{
from: 'test.js',
to: '/dist/test.js',
transform(content, path) {
//what do I write here?
},
}
The simplest approach I could think about is to use several entry points like this:
{
entry: {
a: "./your-main-stuff",
b: "./your-single-file",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js"
}
}
which will create your a.js main bundle and b.js file in __dirname/dist folder both transpiled provided you used corresponding loader(s).
And from copy-webpack-plugin docs section:
webpack-copy-plugin is not designed to copy files generated from the
build process; rather, it is to copy files that already exist in the
source tree, as part of the build process.
so it seems to be difficult (if possible) making it move transpiled files.
Update. If you want to output files into different folders w/o changing your src folder, additonal tools needed. For your case (just 1 file) I would write a simple script and add it into package.json script section combined with webpack call like:
"scripts": {
"dev": "webpack && babel path-to-script.js --out-file path-to-script-compiled.js"
}
Just like in the previous answer, initially I went with the "scripts" entry in package.json that runs babel. But for a number of reasons I wanted to use webpack 5 to do the job. So after failing with webpack-copy-plugin and a good amount of digging around I came to this solution:
let config = {
entry: [
glob.sync(srcDir + '/**/*.js') // get all .js files from the source dir
],
output : {
filename : '[name].rem.js', // webpack wants to bundle - it can bundle here ;)
path: outDir
},
resolve: {
alias: {
'app': appDir
}
},
plugins: [
new RemoveEmptyScriptsPlugin({extensions: ['js'], scriptExtensions: /\.rem\.js/}) // for all .js source files that get bundled remove the bundle .rem.js file
],
module: {
rules:[{
test: /\.jsx?$/,
type: 'asset/resource', // get webpack to take it out instead of bundling
generator: {
filename: ({filename}) => filename // return full file name so directory structure is preserved
},
use: {
loader: 'babel-loader',
options: {
targets: { node: 16 },
presets: [
['#babel/preset-env', { modules: 'commonjs' /* transpile import/export */}],
]
}
}
}]
}
};
// Since the code does not go through the full pipeline and imports are not getting resolved, aliases will remain in the code.
// To resolve them it takes to hack the aliases object into the babel config
config.module.rules[0].use.options.plugins.push(['babel-plugin-webpack-alias-7', {config: {resolve: {alias: config.resolve.alias}}}];
And it does a good job but beware that it takes to use the patched versions of the two plugins (unless the patches have been merged already)!

Moment failed to work with webpack (typescript + babel)

Just as the demo, when i add moment to the project, bundle built by webpack failed, once i remove the moment it is ok.
Note: the module in tsconfig.json is set to es6, which is exactly what i need, when switch it to commonjs, it is ok too.
I am told you guys are acitve here, thanks very much to you~~~
After got it running and disable minify mode, I saw that problem is how you using moment. You can use this config to see your unminify code problem:
entry: './src/index.tsx',
mode: 'production',
output: {
path: path.join(__dirname, 'lib'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.(tsx|ts)$/,
exclude: /node_modules/,
use: ['babel-loader', 'ts-loader'],
},
],
},
optimization: {
// We no not want to minimize our code.
minimize: false
},
After change, I got error moment is not a function. So it's a problem when webpack bundle and how moment export their function
https://github.com/palantir/blueprint/issues/959
You can fix it by
const moment = require('moment')
to use the function as document or
moment.toString()
to just got the current time.
Or change options in tsconfig like in https://momentjs.com/docs/#/use-it/typescript/

Invoke multiple entry points in Webpack

I'm using webpack with typescript and babel to manage my client side web application.
I want to have a vendor.js file for 3rd party scripts, a main.js file, and per-page scripts I can load as needed to provide specific functionality for a page.
All the scripts are compiling as I would expect, but only the vendor.js file is actually getting invoked. The others are compiled, but never invoked.
Below is my webpack.config.js file.
'use strict';
let webpack = require('webpack');
module.exports = {
entry: {
main: './assets/js/main.ts',
"single-page": './assets/js/src/new-loan.ts',
vendor: [
"svg4everybody"
]
},
output: {
filename: './public/assets/js/[name].js'
},
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
})
],
module: {
loaders: [{
test: /\.ts(x?)$/,
loader: 'babel-loader!ts-loader',
exclude: /node_modules/
}]
}
}
And an example of one of the page-specific files. Ideally, when loaded, this file would trigger an alert notification on the page.
webpackJsonp([1],[
/* 0 */
/***/ function(module, exports) {
'use strict';
alert('test');
/***/ }
]);
I can see /why/ the alert isn't triggering (the module function is never invoked), but I can't figure out how to configure webpack to work how I'd like it to.
Thanks for the assistance.
I found that the CommonsChunkPlugin is overwriting the vendor bundle in your config. Try changing the name of CommonsChunkPlugin to common from vendor, and include the common bundle in all of your files, before every other bundle. If you use the CommonsChunkPlugin, only the common bundle will contain the core webpack module loader helper functions, so it must be embedded in every page.
new webpack.optimize.CommonsChunkPlugin({
name: "common",
minChunks: Infinity
})

webpack with external code and owned code bundles

It is easy to create a single bundle for development and deployment with webpack. I'd like to be able to put external dependencies into its own bundle. Thus, I'd have "external-bundle.js" and "index-bundle.js". The problem I have is getting an entire solution to work. I have the working solution, but it needs a small improvement.
I write all of my code using babel and all of it is stored in files with .es extension.
Here is my directory tree:
/index.html
/wap/index.es
/wap/other.es
/lib/index-bundle.js
My webpack.config.js:
var path = require('path');
module.exports = {
entry: { index: './wap/index.es' },
output: {path: 'lib',filename: "[name]-bundle.js"},
module: {
loaders: [
{
loader: 'babel-loader',
test: /\.es$/,
include: [
path.resolve(__dirname, "wap"),
],
}
]
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'redux': 'Redux',
'react-redux': 'ReactRedux',
//'redux-thunk': 'thunkMiddleware', // can't load CommonJS
//'react-google-charts': {
// root: "react-google-charts",
// commonjs2: "Chart",
// amd: "Chart"
//}
},
resolve: {
// allows babel module to find files
extensions: ['', '.js', '.es']
}
};
and babel configuration for completeness:
{
"presets": ["es2015", "react"],
"plugins": ["transform-es2015-modules-commonjs"]
}
I was successful in including external libraries in index.html:
<script src="node_modules/react/dist/react.js"></script>
<script src="node_modules/react-dom/dist/react-dom.js"></script>
<script src="node_modules/redux/dist/redux.js"></script>
<script src="node_modules/react-redux/dist/react-redux.js"></script>
That leaves redux-thunk and react-google-charts. I have failed to include those using html script tag, because those modules are CommonJS only. You can see commented out externals section, because I could not figure out the reference format.
So, my only easy choice was to let those files to get included by webpack, because ES6 code is compiled into commonJS as well.
Ideally I would like to put all external dependencies into external bundle and include it with script tag in HTML file. I'd create a bundle just for code I work on. I was able to use this solution with browserify, but I can't figure this out at all with webpack. I even created a separate configuration file for the external bundle, but my code failed to find objects in the external bundle.
The 2 issues are a bit connected. I need help with:
externalizing redux-thunk and react-google-charts out of my index-bundle.
going to just two bundles
I am not using RequireJS right now and I was hoping not to start using it now.
Thank you

Categories