How to transpile and minify single file with webpack? - javascript

I have a React application, and in my application I'm relying on react-scripts, so the build command is defined like this "build": "react-scripts build", and it works all fine. Now, the point is that inside my src directory I have a JS file called wrapper.js, which is a standalone file, and it is pure JS, no React stuff, but it uses ES6 and some newer features. So, what I want to do is that, I want create a new command, which will transpile and minify this file and will create a standalone copy of it. I thought to use webpack and I created a webpack.config.js file in the root of my project, which looks like this:
const path = require('path');
const MinifyPlugin = require('babel-minify-webpack-plugin');
module.exports = {
mode: 'production',
output: {
path: __dirname + 'build',
publicPath: '/build/',
filename: 'wrapper.js',
},
module: {
rules: [
{
test: /\.js$/,
include: [
path.resolve(__dirname, 'src', 'wrapper.js')
],
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
},
plugins: [
new MinifyPlugin()
]
};
And I added the following to my package.json file "wrapper": "webpack". Now, when I run npm run-scripts wrapper, it executes the webpack command, but it throws error. The output looks like this:
> webpack
Hash: 0aa67383ec371b8b7cd1
Version: webpack 4.19.1
Time: 362ms
Built at: 04/06/2019 10:54:46 AM
1 asset
Entrypoint main = wrapper.js
[0] ./src/index.js 223 bytes {0} [built] [failed] [1 error]
ERROR in ./src/index.js 22:4
Module parse failed: Unexpected token (22:4)
You may need an appropriate loader to handle this file type.
|
| ReactDOM.render(
> <Root />,
| document.getElementById('root'),
| );
What I see is that the problem is that webpack also tries to transpile and minify other files in my src directory, because it seems it has hit my React app's index.js file. How can I exclude everything? Or more precisely, how can I tell webpack to transpile and minify only the file /src/wrapper.js, and not to touch anything else at all?

Lighter weight alternative could be to create a script in your package.json and use babel-minify, https://babeljs.io/docs/en/babel-minify
package.json
{
...
"scripts": : {
"minify": "minify wrapper.js --out-file wrapper.min.js --mangle.keepClassName"
}
...
}

Add entry object to your webpack.config.js.
module.exports={
entry: './src/wrapper.js',
...
}
webpack points the entry object by default to ./src/index.js.
So if you don't override entry object, webpack will bundle the file in ./src/index.js
Update
To point to a output directory properly
output: {
filename: 'wrapper.js',
path: path.resolve(__dirname, 'build')
}

Related

Gulp to Webpack migration on an old website - generating vendors from non-npm dependencies

We want to migrate from gulp to webpack on a rather big and old website. Gulp just concat and minify JS & CSS files at the moment. I want to migrate to webpack because another front dev made some react modules that are compiled by browserify cmd in another folder:
we work in a webapp/ folder, minified files are located here :
js/app.min.js
jslib/vendors.min.js
css/app.min.css
sources files are :
js/
file1.js
file2.js
...
jslib/
jquery-1.11.2.min.js
jquery.fancybox.js
...
jquery.very-old-dependency-not-on-npm.min.js
css/
style.css
extra.css
my goal is to keep this file split for javascript by using two files, app.js and vendors.js. These 2 files should be, ideally, ES6 files that import other files.
Actually i'm looking for a way to do a simple concatenation / minification of JS files through the 2 es6 files with imports. Webpack is checking for dependencies and to overcome the "jquery is not defined" errors i added the webpack.providePlugin and the resolve.alias part in webpack.config. But this loads jquery in my app.min.js file and i don't want it for performance purposes. I'm aware that all libs should be npm installed and required/imported in es6 context for an optimized webpack use, but some old jquery libs are not available on npm in the version i want so i have to go with the files already in the project.
vendors.js :
import "./jquery-1.11.2.min.js";
import "./jquery.fancybox.js"
app.js :
import "./file1.js"
import "./file2.js"
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
'/../../js/app': path.resolve(__dirname + '/../../js/app.js'),
'/../../jslib/vendors': path.resolve(__dirname + '/../../jslib/vendors.js'),
},
output: {
path: path.resolve(__dirname),
filename: '[name].min.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
],
resolve: {
alias: {
jquery: path.resolve(__dirname+'/../../jslib/jquery-1.11.2.min.js')
},
},
}
all my paths are ok (the folder location of the current gulp / future webpack is not in assets folder ) and webpack is successfully running, i just want to find a clean way to perform a simple concat / minify without including jquery in my app file before going further.
Here is an example of using jquery in an html file external
As for the names of app and vendors, you must change this part
entry: {
'/../../js/app': path.resolve(__dirname + '/../../js/app.js'),
'/../../jslib/vendors': path.resolve(__dirname + '/../../jslib/vendors.js'),
},
for this
entry: {
app: path.resolve(__dirname + '/../../js/app.js'),
vendors: path.resolve(__dirname + '/../../jslib/vendors.js'),
},
Do not add jquery to import if you want it to be added from outside. Remove jquery from vendors.js
I hope I helped.

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)!

Webpack ERROR in missing path

I am getting an error whenever I try to run Webpack. It just says ERROR in missing path. The error goes away when I remove the module key in the configuration below:
module.exports = {
entry: './client/scripts/app.js',
output: {
path: './docs/scripts/',
filename: 'bundle.js'
},
module: {
loaders: [{
test: /\.js$/,
loader: 'ng-annotate!',
exclude: /node_modules|docs\/bower_components/
}],
}
};
Here is the error output:
Hash: 396f0bfb9d565b6f60f0
Version: webpack 1.13.1
Time: 76ms
+ 1 hidden modules
ERROR in missing path
My webpack config sits in the root of my project. The folder structure is below:
client
scripts
app.js
node_modules
docs
scripts
bundle.js
bower_components
webpack.config.js
You have mistake in RegExp, add \ before /
/node_modules|docs\/bower_components/
^^
also after ng-annotate you don't need add !, because you are using only one loader
loader: 'ng-annotate'
^^

Webpack - Copy images files

I'm currently using Webpack to pack our Angular2 application and i'm facing a problem.
I've read several documentations but I can't achieve how to copy some files in my output directory using the file loader.
Here is my current file hierarchy :
config
| - webpack.common.js
app
|- static
| - css
| - ...
| - fonts
| - ...
| - img
| - someimage.png
| - anotherimage.png
|- main.ts
and the (full) webpack.common.js :
var path = require("path")
var webpack = require("webpack")
var ExtractTextPlugin = require("extract-text-webpack-plugin")
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './app/main.ts',
},
output: {
filename: 'js/[name].js',
path:'./built',
chunkFilename: 'bundles/[id].chunk.js'
},
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts',
exclude:'./out/'
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
include: [
path.resolve(__dirname, '/app/static/img/'),
],
loader: 'file?name=[path][name].[ext]&context=./src',
}
]
},
resolve: {
extensions: ['', '.js', '.ts', '.gif']
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
To execute webpack I play the command :
webpack --config Config/webpack.common.js --bail
The ts file are correctly transpilled into javascript and copied into the output directory, the index.html file is also present but there is none of my image files.
I think there is something wrong in my configuration file but I can't see what. I'm banging my head on it fore many hours so any help will be much appreciated.
Thank you
Creating separate entry point for images may not be what you want, depending on how you build CSS part of the project. As alternative you can copy static files with copy-webpack-plugin or grunt / gulp task.
You should use url-loader to load images. Sample code is given below.
{
module: {
loaders: [
{ test: /\.(jpe?g|png|gif|svg)$/i, loader: 'url?limit=10000!img?progressive=true' }
]
}
}
Are you referring the gif files or corresponding css/sass files inside your entry js file.
entry: {
app: './app/main.ts',
}
Webpack will load all the files which have a reference in the entry point. If all your files are not in one entry point. Then you can add multiple entry points as shown below.
entry: {
app: './app/main.ts',
image: './app/something.ts'
}
Also, i would put webpack.config.js file directly in the root directory to have better access to the whole ecosystem. Try moving it from config folder to root folder.

How to load library source maps using webpack?

I'm building two projects with webpack; one is a library for the other.
Is it possible to consume the sourcemaps from my library project when building my wrapper project? I would like the ability to debug my library code from my wrapper UI.
My build works correctly in that the library is built in. The only issue is sourcemaps. The JavaScript I see in the browser debugger is uglified, because sourcemaps are unavailable.
Snippet of my project structure:
+-- my-ui/
+-- dist/
+-- my-ui.js
+-- my-ui.js.map
+-- node_modules/
+-- my-lib/
+-- dist/
+-- bundle.js
+-- bundle.js.map
Snippet from webpack.config.js:
module.exports = {
entry: './src/js/main.jsx',
output: {
path: path.join(__dirname, 'dist'),
filename: 'my-ui.js',
library: 'my-ui',
libraryTarget: 'umd'
},
devtool: 'source-map',
module: {
loaders: [
{test: /\.jsx?$/, loader: 'babel', include: path.join(__dirname, 'src')}
]
},
plugins: [
new Clean('dist'),
new HtmlWebpackPlugin({
template: 'src/index.html',
inject: true
})
]
};
I finally figured out my issue...
Thanks to #BinaryMuse for the tip on source-map-loader. This indeed was the right way to go, though it wasn't working for me initially.
What I eventually realized is that I need to enable the source-map-loader for webpack in both "my-lib" and "my-ui". Without source-map-loader in "my-lib" webpack config, the source-map-loader inside "my-ui" errors (with a warning message sadly) because it cannot locate source maps for transitive dependencies of "my-lib". Apparently the source maps are so good that source-map-loader is able to peek at all aspects of the dependency tree.
Also of note, I ran into an issue using source-map-loader in conjunction with react-hot-loader. See, react-hot-loader does not include source maps. When source-map-loader tries to find them (because it's just scanning everything), it cannot and aborts everything.
Ultimately, I'd like source-map-loader to be more fault tolerant, but when set up correctly, it does work!
devtool: 'source-map',
module: {
preLoaders: [
{test: /\.jsx?$/, loader: 'eslint', exclude: /node_modules/},
{test: /\.jsx?$/, loader: 'source-map', exclude: /react-hot-loader/}
],
loaders: [
{test: /\.jsx?$/, loader: 'raect-hot!babel', exclude: /node_modules/}
]
}
My answer is similar to #Jeff Fairley's and I had the same directory structure, with the only difference being that I was using module: { rules: [] } instead of his module: { preLoaders: [..], loaders: [...]}. This is what I had to add to my webpack.config.js file:
mode: 'development',
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.js$/,
enforce: "pre",
use: ["source-map-loader"],
}
]
},
Then I ran
npm i -D source-map-loader
and I saw the TypeScript source code of the dependency I was using when clicking through tracebacks in Chrome's devtools. See the Webpack docs for source-map-loader.
I am using create-react-app and this is how I Fixed it (without running eject cmd)
Note : If your app is already overriding webpack config using react-app-rewired you can ignore first three steps.
npm i react-app-rewired -D - This will help you to override webpack
configuration.
package.json - change your scripts, replace react-scripts with react-app-rewired
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
}
config-overrides.js - create this file in the parent level of the app.
npm i source-map-loader -D - To load source maps (assuming that your lib's dist has source map file). It doesn't matter which build tool(ex: Rollup, webpack or parcel) you use to generate sourcemap.
Copy below code in config-overrides.js
module.exports = {
webpack: (config, env) => {
// Load source maps in dev mode
if (env === 'development') {
config.module.rules.push({
test: /\.(js|mjs|jsx|ts|tsx)$/,
use: ['source-map-loader'],
enforce: 'pre',
});
// For `babel-loader` make sure that sourceMap is true.
config.module.rules = config.module.rules.map(rule => {
// `create-react-app` uses `babel-loader` in oneOf
if (rule.oneOf) {
rule.oneOf.map(oneOfRule => {
if (
oneOfRule.loader &&
oneOfRule.loader.indexOf('babel-loader') !== -1
) {
if (oneOfRule.hasOwnProperty('options')) {
if (oneOfRule.options.hasOwnProperty('sourceMaps')) {
// eslint-disable-next-line no-param-reassign
oneOfRule.options.sourceMaps = true;
}
}
}
});
}
return rule;
});
}
return config;
},
};
Restart your app (if it's already running). source files get loaded in different locations, based on path in map file. Check all folders patiently :)
Note :
1. Your source maps get loaded in one of the folder(ex : localhost:3000 or webpack:/// ) based on path it reads from xxx.js.map file.
2. If you are using rollup for your libs, please make sure you give proper path in the configuration file (output.sourcemapPathTransform ), This will help to load sourcemaps in the proper location.
You should be able to use any of the eval source map options that Webpack provides.
Really that just amounts to setting the right devtool option in your webpack.config.js for the my-lib project.
devtool: 'eval',
eval and eval-source-map should both work.
See the Webpack documentation for the various options.

Categories