Webpack Plugin
A webpack plugin which runs JSDefender on every chunk using the compiler's "emit" hook.
Supported webpack versions: ^3.0.0
, ^4.0.0
, ^5.0.0
Installation
Install the plugin to your project:
npm install <package-directory>/preemptive-jsdefender-core-{version}.tgz <package-directory>/preemptive-jsdefender-webpack-plugin-{version}.tgz --save-dev
OR
yarn add file:<package-directory>/preemptive-jsdefender-core-{version}.tgz file:<package-directory>/preemptive-jsdefender-webpack-plugin-{version}.tgz --dev
@preemptive/jsdefender-core
as its peerDependency; this is why you must also install it.
Usage
Production Mode and Minifiers
By default, when you run webpack in production mode, it applies optimizations that remove JSDefender inline protection directives from the code. As a result, JSDefender will not be able to use your inline protection settings. To prevent webpack from stripping the directives, you need to modify its optimization settings. Though you can turn off optimization entirely, we do not encourage that. Instead, change the default minification to one that preserves directives.
The following webpack.config.js
code snippet utilizes the terser-webpack-plugin
:
const TerserPlugin = require("terser-webpack-plugin");
//...
module.exports = {
// Use the other settings as you would do
// ...
// Modify optimization settings
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({ // Use TerserPlugin as a minifier
terserOptions: {
compress: { // Preserve directives
directives: false,
},
},
}),
],
},
// ...
// Other settings
}
Plain Webpack
Import the jsdefender-webpack-plugin
in your webpack config (by default it is called webpack.config.js
) and add it to the plugins
array:
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
// --Other configuration
plugins: [
// --Other plugins
new JSDefenderWebpackPlugin({
configurationFile: 'jsdefender.config.json', // Optional path to the configuration file, if any, it defaults to `jsdefender.config.json`
quietMode: false, // If false every log message will be displayed, otherwise only the errors and warnings, it defaults to false
enableInDevelopmentMode: false, // If false the protection will be skipped in non-production modes, otherwise it will run in every mode, it defaults to false
includeChunks: [ 'app', 'util' ], // The chunk names that should be protected, by default every chunk is included
excludeChunks: [ 'vendor', 'util' ], // The chunk names that should not be protected, be default nothing is excluded
/* Other JSDefender options e.g. `settings: { booleanLiterals: true, stringLiterals: true }` etc. could also be provided here */
})
]
enableInDevelopmentMode
option of the plugin to true
.
booleanLiterals: true
provided in the configuration file and booleanLiterals: false
provided directly in the plugin's constructor, then the final value is going to be booleanLiterals: false
.
includeChunks
and excludeChunks
then the exclusion takes precedence.
Angular
Angular hides the webpack config from the developers by default and it can be modified in different ways for the different versions of Angular.
The Webpack configuration of the JSDefender Webpack Plugin is the same for every Angular version:
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
...
module.exports = {
...
plugins: [
new JSDefenderWebpackPlugin({
/* All the configuration from the above example can be used here, plus: */
excludeChunks: [ 'vendor', 'scripts', 'common', 'runtime', 'polyfills', 'polyfills-es5', 'styles', 'inline' ] // vendor chunk should be always excluded, but other system chunks could also have problems, especially the polyfills
})
]
...
}
Be sure to not include the vendor
chunk because it has some deprecated code in it that JSDefender cannot protect. If you still encounter problems after excluding the vendor
chunk, then you should try to exclude other system chunks starting with the polyfills
and polyfills-es5
, but if you want maximal performance, exclude every system chunk specified in the example config above. The exclusion can be achieved either by including the app's chunks one-by-one, or by excluding the appropriate chunks. Of course you can also include or exclude any other chunk.
The provided configurations should work either with Angular or Angular Universal (Server Side Rendering).
Tested Angular versions: ^5.0.0
, ^8.0.0
, ^9.0.0
, ^11.0.0
, ^14.0.0
Angular 2-5
In order to be able to edit the webpack.config.js
in an Angular 2-5 project, it must be ejected first because it is hidden by default. To eject, run the following command:
ng eject
After ejecting, add the JSDefenderWebpackPlugin
to the plugins array in the webpack.config.js
as shown in the Angular section.
Then build or serve the project by using the --vendor-chunk=true
flag in --prod
mode to generate a separate vendor bundle so you can exclude it from protection. In a development build, the vendor chunk is automatically generated.
ng build --prod --vendor-chunk=true
ng serve --prod --vendor-chunk=true
Now you should see the protected chunks.
Angular 6+
Since Angular 6+, the webpack.config.js
is not editable and it cannot be ejected, but custom Angular builders can be used to modify the webpack config.
Create a file called webpack.partial.js
in your Angular project and add the configuration there as shown in the Angular section.
Enable the creation of the separate vendor
chunk in production builds by either setting the "vendorChunk": true
in the angular.json
's production build configuration or by adding the --vendorChunk=true
flag to the build script. Learn more at the ng build documentation.
Using the custom-webpack
builder
The @angular-builders/custom-webpack builder makes it possible to provide a path to a custom webpack configuration in the angular.json
file. There are other builders you can use to extend Angular's Webpack configuration, but we recommend custom-webpack
. Make sure to use the version compatible with your Angular version!
Install the package:
npm install --save-dev @angular-builders/custom-webpack@{version}
OR
yarn add --dev @angular-builders/custom-webpack@{version}
Modify the project's angular.json
configuration:
"architect": {
...
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.partial.js"
},
...
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "{project-name}:build" // Change {project-name} to your project's name
}
}
Then build or serve your Angular project and see the protected chunks.
Using the ngx-build-plus
builder
The ngx-build-plus builder makes it possible to provide a custom webpack configuration through the Angular CLI (ng
) with the --extra-webpack-config
flag. Make sure to use the version compatible with your Angular version!
Install the package via the Angular CLI:
ng add ngx-build-plus@^{your-angular-major-version}.0.0
The next step is to add the partial webpack configuration to your build, or run scripts with the --extra-webpack-config
flag in your package.json
:
"scripts": {
"build:prod": "ng build --extra-webpack-config webpack.partial.js --prod",
"serve:prod": "ng serve --extra-webpack-config webpack.partial.js --prod",
}
Then build or serve your Angular project and see the protected chunks.
Ionic
Ionic hides the webpack config from the developers by default, but it can be modified in different ways for the different versions of the framework.
Tested Ionic + front-end framework version pairs:
- Ionic
^3.0.0
+ Angular^5.0.0
- Ionic
^5.0.0
+ Angular^8.0.0
- Ionic
^5.0.0
+ React^16.0.0
Ionic 2-3
In Ionic 2-3 you can provide your own webpack config which can extend Ionic's own configuration.
First, add "ionic_webpack"
configuration to the "config"
section in the project's package.json
.
{
...
"config": {
"ionic_webpack": "./webpack.partial.js"
}
...
}
Then, add a file called webpack.partial.js
to the project root and add the following configuration. Save the file, then configure the JSDefenderWebpackPlugin
as in the Angular section above.
const { dev, prod } = require('@ionic/app-scripts/config/webpack.config');
const webpackMerge = require('webpack-merge');
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
const webpackJSDefenderConfig = {
plugins: [
new JSDefenderWebpackPlugin({
/* Same configuration as in the Angular section above, make sure to exclude the vendor chunk */
})
]
};
module.exports = {
dev: webpackMerge(dev, webpackJSDefenderConfig),
prod: webpackMerge(prod, webpackJSDefenderConfig)
};
The vendor
chunk is always generated for Ionic 2-3 projects, so no additional configuration is needed other than excluding it in the JSDefenderWebpackPlugin
config.
Ionic 4+
Since Ionic 4, there is no custom webpack config created by Ionic. Instead, the inner front-end framework's (Angular, React, Vue) own solution is used.
Angular 6+
The configuration from the above Angular 6+ section can be used for Ionic 4+ and Angular 6+ projects. So a custom builder needs to be added to extend the default webpack config.
React 16+
To circumvent the predicament of no custom webpack config available, there are two possible ways to make an Ionic-React project work with the webpack plugin (use either one, not both of them!):
1. Eject
Eject from the managed React environment with npm run eject
or npx react-scripts eject
.
Proceed with the plugin installation outlined above under Usage -> Plain Webpack.
2. Use react-app-rewired
There is a third-party package that helps in accessing the Webpack configuration options (link). This is an added dependency, nevertheless a necessary one.
- Install the package to your project:
npm install react-app-rewired --save-dev
- Add the following to your package.json scripts (modify existing script keys if necessary):
"start": "react-app-rewired start",
"build": "react-app-rewired build",
- Add a
config-overrides.js
to the root of the project with the following lines:
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
module.exports = function override(config, env) {
if (!config.plugins) {
config.plugins = [];
}
config.plugins.push(
new JSDefenderWebpackPlugin({
configurationFile: './jsdefender.config.json',
excludeChunks: [ 'vendor', 'scripts', 'common', 'runtime', 'polyfills', 'polyfills-es5', 'styles' ]
})
);
return config;
}
- Set up a JSDefender configuration according to the configuration guide below.
- Finally, run
npm run build
oryarn build
to have the webpack plugin protect your code.
Configuration
The JSDefender webpack plugin accepts the same configuration object as the JSDefender configuration file. You can learn more about it at the documentation home. The plugin uses a default configuration just as JSDefender CLI does. This can be overridden by explicitly specifying a configuration to the plugin.
configurationFile
is present, its individual configuration lines will be overwritten by the JSDefender configuration directly passed to the new JSDefenderWebpackPlugin()
constructor in the webpack.config.js
or webpack.partial.js
Webpack config file. For example, if you set booleanLiterals: true
in the JSDefenderWebpackPlugin
constructor but there is a configurationFile
set which has booleanLiterals: false
, then the first will take precedence, so the final value will be booleanLiterals: true
.