How to find unused files in a Webpack project? - javascript

I have a JavaScript project that is built with Webpack which I know has a lot of dead code files. How can I find source files that aren't used in the project?

There are a few plugins, but the UnusedFilesWebpackPlugin appears to be the most popular.
An example use is:
new UnusedFilesWebpackPlugin({
failOnUnused: environment !== 'development',
patterns: ['src/components/**/*.jsx', 'src/store/**/*.js', 'sass/**/*.scss'],
ignore: ['**/LocalVideoDemo.jsx'],
})
This will check for unused JSX files in the components directory, unused JS files in the Redux store directory and unused SCSS files. It will ignore whether or not the LocalVideoDemo.jsx file is included.

Tried using various webpack plugins, but ran into out of memory issues with each plugin. I think the easiest solution for a create-react-app bootstrapped application is to use ESLint.
Use the no-unused-modules which is now a part of eslint-plugin-import.
After setting up eslint, installing eslint-plugin-import, add the following to the rules:
"rules: {
...otherRules,
"import/no-unused-modules": [1, {"unusedExports": true}]
}

Related

Nest.js bundle size too large by default

Upon running nest build command, I was expecting that the build would pick up only the imported modules from the node_module folder.
However, the command did not pick up only the relevent files and minifiy them. Instead, it picked up the node_modules directly from the root folder.
As a result, the final build size of the application is extremely large.
How can I reduce the bundle size?
I'm not sure that firing you from your job would solve your company's problems.
However, did you try the nest build --webpack command instead of the nest build command?
Maybe we would need some more informations about the project.
Did you also try to check this page on the documentation that says:
The webpack options file can contain standard webpack configuration
options. For example, to tell webpack to bundle node_modules (which
are excluded by default), add the following to webpack.config.js:
module.exports = {
externals: [],
};
If you have a webpack.config.js on your project, try removing the externals key from it.

How to //#ts-check globally on my react application

I'm using create-react-app to test a monorepo that i'm creating. Inside the monorepo I have some index.d.ts files to declare my packages however I do not wish to start using .ts files and would prefer to stick to .js files.
I am using the create-react-app as a means to test my packages before publishing them as well as ensuring that the Typescript declaration files are working correctly.
Within my create-react-app I am forced to use //#ts-check at the top of the App.js file before Typescript starts to check my work.
]2
I would like the ability to enable a //#ts-check globally for my create-react-app's as I plan on using it on all my future and passed projects and adding the //#ts-check at top of every .js file sounds extremely tedious.
I have tried using a tsconfig.json as shown in this answer however it did not work.
This picture below is showing the same App.js file without the //#ts-check and with tsconfig.json and as you can see it is no longer checking my file.
I'm fairly new to Typescript so could somebody please explain to me how I could enable //#ts-check globally on my create-react-app projects.
Thanks in advance!
Add a file at the root of your project with the filename:
jsconfig.json
Add the following to the contents of that file:
{
compilerOptions: {
"checkJs": true,
"jsx: "react"
}
}
Reference

How to make Webpack use project's "node_modules" in js scripts located outside the project folder?

I have Node.js project and the following structure of folders:
lib
awesome-formatter.js
FrontEndApp
prettify.js
node_modules
awesome-parser
BackEndApp
...
I use awesome-parser module and awesome-formatter.js library in prettify.js script like this:
require('awesome-parser')
require('../lib/awesome-formatter.js')
awesome-formatter.js, in turns, should use awesome-parser too:
require('awesome-parser')
My FrontEndApp has been configured to use Webpack, and I'm trying to run it in dev mode using npm run dev command. However, I got the following error:
ERROR Failed to compile with 1 errors
These dependencies were not found:
* awesome-parser in /home/user/dev/lib/awesome-formatter.js
I don't want to move awesome-formatter.js inside the FrontEndApp because I also use it in BackEndApp project (and probably in some other projects) and I don't want to create separate "node_modules" in "lib" for it just not to duplicate installed modules on disk.
So, my question is, how to make Webpack use project's "node_modules" in js scripts located outside the project folder?
P.S. Of course there are workarounds like symlinks or making a full-featured module (with package.json etc.) from lib/awesome-fromatter and installing it into FrontEndApp/node_modules, but is there a direct way to solve this problem?
I've found a solution: resolve.modules sould be added to Webpack configuration file.
module.exports = {
...
resolve: {
...
modules: [
'node_modules',
resolve('node_modules')
]
},
...
}
This means that Webpack is searching modules in 'node_modules' as a relative subfolder (and it's the usual behavior), and at the absolute path to the project's 'node_modules' as well: resolve('node_modules'), so that scripts in folders outside the project (like lib in my structure) can find and use it.

How to manage configuration for Webpack/Electron app?

I am using Webpack 2 and Electron to build nodejs application on Mac.
In my project in the root I have directory 'data' where I store configuration in a json like data/configurations/files.json (in practices there are different files with dynamic names)
After webpackaing though when I call: fs.readdirSync(remote.app.getAppPath()); to get files in the root I get only these packed: [ "default_app.js", "icon.png", "index.html", "main.js", "package.json", "renderer.js" ]
path.join(remote.app.getAppPath(), 'data/tests/groups.json'); called with FS ReadSync leads to an issue Error: ENOENT, data/tests/groups.json not found in /Users/myuser/myproject/node_modules/electron/dist/Electron.‌​app/Contents/Resourc‌​es/default_app.asar. So it seems that the whole data folder is not picked up by webpacker.
Webpack config is using json-loader and I did not find any documentation mentioning anything special about including specific files or jsons. Or do I have to reference json files in my code differently as they might be packed under main.js.
What is the best practice for Electron/Webpack for managing JSON config files? Am I doing something wrong when webpacking the project?
My project is based of https://github.com/SimulatedGREG/electron-vue using webpack/electron/vue
The Webpack Misconception
One thing to understand upfront is that webpack does not bundle files required through fs or other modules that ask for a path to a file. These type of assets are commonly labeled as Static Assets, as they are not bundled in any way. webpack will only bundle files that are required or imported (ES6). Furthermore, depending on your webpack configuration, your project root may not always match what is output within your production builds.
Based on the electron-vue documentation's Project Structure/File Tree, you will find that only webpack bundles and the static/ directory are made available in production builds. electron-vue also has a handy __static global variable that can provide a path to that static/ folder within both development and production. You can use this variable similar to how one would with __dirname and path.join to access your JSON files, or really any files.
A Solution to Static Assets
It seems the current version of the electron-vue boilerplate already has this solved for you, but I'm going to describe how this is setup with webpack as it can apply to not only JSON files and how it can also apply for any webpack + electron setup. The following solution assumes your webpack build outputs to a separate folder, which we'll use dist/ in this case, assumes your webpack configuration is located in your project's root directory, and assumes process.env.NODE_ENV is set to development during development.
The static/ directory
During development we need a place to store our static assets, so let's place them in a directory called static/. Here we can put files, such as JSONs, that we know we will need to read with fs or some other module that requires a full path to the file.
Now we need to make that static/ assets directory available in production builds.
But webpack isn't handling this folder at all, what can we do?
Let's use the simple copy-webpack-plugin. Within our webpack configuration file we can add this plugin when building for production and configure it to copy the static/ folder into our dist/ folder.
new CopyWebpackPlugin([
{
from: path.join(__dirname, '/static'),
to: path.join(__dirname, '/dist/static'),
ignore: ['.*']
}
])
Okay so the assets are in production, but how do I get a path to this folder in both development and production?
Creating a global __static variable
What's the point of making this __static variable?
Using __dirname is not reliable in webpack + electron setups. During development __dirname could be in reference to a directory that exists in your src/ files. In production, since webpack bundles our src/ files into one script, that path you formed to get to static/ doesn't exist anymore. Furthermore, those files you put inside src/ that were not required or imported never make it to your production build.
When handling the project structure differences from development and production, trying to get a path to static/ will be highly annoying during development having to always check your process.env.NODE_ENV.
So let's simplify this by creating one source of truth.
Using the webpack.DefinePlugin we can set our __static variable only in development to yield a path that points to <projectRoot>/static/. Depending if you have multiple webpack configurations, you can apply this for both a main and renderer process configuration.
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '/static').replace(/\\/g, '\\\\')}"`
})
In production, we need to set the __static variable manually in our code. Here's what we can do...
index.html (renderer process)
<!-- Set `__static` path to static files in production -->
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<!-- import webpack bundle -->
main.js (main process)
// Set `__static` path to static files in production
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
// rest of application code below
Now start using your __static variable
Let's say we have a simple JSON file we need to read with fs, here's what we can accomplish now...
static/someFile.json
{"foo":"bar"}
someScript.js (renderer or main process)
import fs from 'fs'
import path from 'path'
const someFile = fs.readFileSync(path.join(__static, '/someFile.json'), 'utf8')
console.log(JSON.parse(someFile))
// => { foo: bar }
Conclusion
webpack was made to bundle assets together that are required or imported into one nice bundle. Assets referenced with fs or other modules that need a file path are considered Static Assets, and webpack does not directly handle these. Using copy-webpack-plugin and webpack.DefinePlugin we can setup a reliable __static variable that yields a path to our static/ assets directory in both development and production.
To end, I personally haven't seen any other webpack + electron boilerplates handle this situation as it isn't a very common situation, but I think we can all agree that having one source of truth to a static assets directory is a wonderful approach to alleviate developer fatigue.
I think the confusion, (if there is any), might come from the fact that webpack not only "packs", embeds, things, code, etc... but also process content with its plugins.
html plugin being a good example, as it simply generates an html file at build-time.
And how this relates to the config file issue?,
well depending on how you are "requiring" the "config" file, what plug-in you are using to process that content.
You could be embedding it, or simply loading it as text, from file system or http, or else...
In the case of a config file, that I guess you want it to be parsed at runtime,
otherwise it's just fancy hardcoding values that perhaps you could be better simply typing it in your source code as simple objects.
And again in that case I think webpack adds little to nothing to the runtime needs, as there is nothing to pre-pack to read at later use,
so I would possibly instead or "require"it, i'll read it from the file system, with something like :
// read it parse it relative to appPath/cwd,
const config = JSON.parse(
fs.readfileSync(
path.join( app.getAppPath(), "config.json" ),
"utf-8"
))
//note: look fs-extra, it does all that minus the app.path plus async
and electron will read it from the file system , or if using Electron.require will read it from asar|fileSystem (in that order if I remember correctly, I could be wrong),
Webpack design philosophy is focused around very simple yet powerful concept:
Transform and bundle everything that is actually used by your app.
To achieve that webpack introduces a powerful concept of dependency graph, which is able to manage virtually any kind of dependencies (not only *.js modules) by the means of so-called loaders.
The purpose of a loader is to transform your dependency in a way that makes statement import smth from 'your_dependency' meaningful. For instance, json-loader calls JSON.parse(...) during loading of *.json file and returns configuration object. Therefore, in order to take advantage of webpack dependency resolution system for managing JSONs, start from installing json-loader:
$ npm install --save-dev json-loader
Then modify your webpack.config.js in the following way:
module.exports = {
...
module: {
rules: [
{test: /\.json$/, use: 'json-loader'}
]
}
...
};
At this point webpack should be able to resolve your JSON dependencies by their absolute paths, so the following should work (I assume here that you have a subdirectory config of your root context dir, containing file sample.json):
import sampleCfg from './config/sample.json';
But importing physical paths doesn't lead to elegant, robust and maintainable code (think of testability, for example), so it is considered a good practice to add aliases to your webpack.config.js for abstracting away your physical .config/ folder from your import statements
module.exports = {
...
resolve: {
alias: {
cfg: './config'
}
}
...
}
Then you'll be able to import your JSON config like that:
import sampleCfg from 'cfg/sample.json'
Finally, if you use SimulatedGREG/electron-vue Electron project template (as you mentioned in your post), then you have three webpack configuration files:
.electron-vue/webpack.web.config.js - use this config file if you use this template just for ordinary web development (i.e. not for building native Electron projects);
.electron-vue/webpack.main.config.js - use this file to configure webpack module that will run inside Electron's main process;
.electron-vue/webpack.renderer.config.js - use this file for Electron's renderer process.
You can find more information on main and renderer processes in the official Electron documentation.

webpack 1: resolve.alias not aliasing one npm module to another

I have a webpack config used to create a server bundle for an isomorphic React app. Inside of that config, I’m trying to use resolve.alias to alias one module (parse) as another (parse/node). This handy chart about resolve.alias in webpack's documentation makes it seem like this should be possible. This is what my alias object looks like:
alias: {
parse: 'parse/node',
pubnub: 'static/deps/pubnub/pubnub-3.13.0.min.js'
}
I’m already using alias for the module pubnub, and that alias works at intended. Unfortunately, whatever I do to the parse key doesn’t seem to change the resulting require in my webpack-built bundle.js:
/***/ function(module, exports) {
module.exports = require("parse");
/***/ },
I’m trying to resolve to /node_modules/parse/node.js. Using absolute/relative paths doesn’t seem to work, nor added a .js extension to the end. At this point, I can’t figure out if this is a webpack bug or something I’m overlooking. Any help would be greatly appreciated! Here’s a link to a gist with my full webpack config: https://gist.github.com/daleee/a0025f55885207c1a00a
node#5.7.0
npm#3.7.5
webpack#1.12.14
The issue was with the following line in the webpack configuration:
...,
externals: [nodeExternals({
whitelist: ['webpack/hot/poll?1000']
})],
...,
webpack-node-externals is a webpack plugin that is used to automatically blacklist modules found in node_modules. There is usually no need to bundle npm modules in with your final bundle when building for the backend, as the npm modules are installed on the system. This blacklist also prevented webpack from doing any aliasing to any npm modules. Changing the whitelist array like so solved my issue:
...,
externals: [nodeExternals({
whitelist: ['webpack/hot/poll?1000', 'parse']
})],
...,
The moral of this story: don't copy code from boilerplates without fully understanding what it does. I could have saved a few days of headache if I had simply just read the README.md for the webpack plugins I was using.

Categories

Resources