JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 39
  • Score
    100M100P100Q61875F
  • License MIT

CodeCoupler Webpack Externals Factory

Package Exports

  • @codecoupler/cc-webpack-externals-plugin

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@codecoupler/cc-webpack-externals-plugin) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Webpack Externals Configuration Concept

What is this

Let's start with an example: You are developing a application, bundled with Webpack, that requires jQuery and Bootstrap. The core library of your application should not contain jQuery or Bootstrap. These libraries should be loaded separately. We also assume that the HTML files will be created or bundled with the html-webpack-plugin.

What you have to do normally:

You start of course with installing the modules with npm i jquery and npm i bootstrap. In your code you use then something like var $ = require('jquery'); or import $ from 'jquery' or even require('bootstrap');. After that you add these modules into the externals configuration of webpack, so they will not get bundled with your library. Then you modify your HTML files to load the external libraries from a folder or from a CDN. Last but not least you have to write into your documentation what libraries are needed to use your library and how to include them.

What you have to do with this plugin:

With this plugin you can just install these modules with npm i some_module and define these modules in a simple configuration file. The most of the other steps will be executed automatically.

It is even more easier if needed:

Furthermore you can skip even the modification of the configuration file. Under the NPM namespace @cc-external are packages available that will set the configuration automatically. For example instead installing jQuery with npm i jquery you can just simple type npm i @cc-external/jquery and everything is done. You can use jquery and it will be automatically setup as external library.

And some more feature:

  1. Switch from self-hosted external files to CDN-Version with just one configuration option.

  2. If you include your configuration file in your package a developer using your library and this plugin just have to configure your library as external. All externals configurations of your library will be merged into the configuration of the developer using your library.

  3. You can set an alias to the module that will be externalized, so you can instead import $ from jquery you could do import $ from whatever. This is also useful if you want to modify an existing library, but use the origin module name.

Examples

Example 1: (The fast way) Bootstrap + jQuery

Install the packages:

npm i @cc-external/bootstrap
npm i @cc-external/jquery

And you are ready to start!

Example 2: (With own configuration) Bootstrap + Font Awesome + jQuery + animate.css

Install the packages:

npm i bootstrap
npm i @fortawesome/fontawesome-free
npm i jquery
npm i animate.css

Put this in webpack.externals.js:

module.exports = [
  { module: "jquery", global: "$", entries: "dist/jquery.min.js" },
  { module: "animate.css", entries: "animate.min.css" },
  {
    module: "@fortawesome/fontawesome-free",
    entries: "css/all.min.css",
    copy: ["webfonts/"],
  },
  {
    module: "bootstrap",
    entries: ["dist/css/bootstrap.min.css", "dist/js/bootstrap.bundle.min.js"],
  },
];

Example 3: (Modified Bootstrap) Bootstrap + Font Awesome + jQuery + animate.css

Clone, modify and build jQuery under your own name like my-jquery. Now you want to use this modified version in an other project.

Remove the origin jQuery:

npm remove jquery

Install your package:

npm i my-jquery

Put this in webpack.externals.js:

module.exports = [
  {
    module: "my-jquery",
    alias: "jquery",
    global: "$",
    entries: "dist/jquery.min.js",
  },
];

And you do not have to change the import statements. import $ from "jquery" will work and use your modified library.

Setup

Install package:

npm i @codecoupler/cc-webpack-externals-plugin -D

Add to your Webpack configuration:

const ccWebpackExternalsPlugin = require("@codecoupler/cc-webpack-externals-plugin");
module.exports = {
  plugins: [new ccWebpackExternalsPlugin(options)],
};

The options argument is optional and can be an object. You can specify the following options:

  • packagesPath: (string) The subfolder name where to copy all the assets (default: "vendor")
  • useCdn: (boolean) If true no assets will be copied and CDN links will be injected instead (default: false)
  • getCdnPath: (function(name, version, path): string) This function should return a string to inject as CDN link to the assets (default: (name, version, path) => //cdn.jsdelivr.net/npm/${name}@${version}/${path})

The configuration file on detail

You have to define every module that should be handled as external in a file named webpack.externals.js in your root project folder. An absolute minimum file content would be:

module.exports = [];

As you can see an array will be defined here. Every item in this array is an object which define an external module. The available options for every entry are:

  • module: The name of the module (corresponds to the package name).
  • alias: Optional. The module name you can use in your import and require statements instead of the module name.
  • global: Optional. The global variable name to access the library provided by the node module.
  • entries: Optional. String or array of strings. Name of js or css files to copy from the modules folder to the dist folder and inject links to them into your HTML files.
  • copy: Optional. String or array of strings. Globs to copy from the modules folder to the dist folder. This can be used to copy assets like fonts.
  • includeExternals: If true the webpack.externals.js of the module will be read and merged (default is true).

The order and dependencies

You have to pay attention to the order. All the injections will be done in the order defined in the configuration file. Because Bootstrap depends on jQuery you have to place the module bootstrap after jquery.

If you want to add just an hint on what a module depents you can add just an entry only with the module name:

module.exports = [
  { module: "jquery" },
  {
    module: "bootstrap",
    entries: ["dist/css/bootstrap.min.css", "dist/js/bootstrap.bundle.min.js"],
  },
];

This configuration tells webpack to copy the bootstrap assets but let the module jquery untouched. This is useful if you want to leave the decission up to the user of your library if jquery should be bundled or not (and what assets exactly should be injected). However putting the module jquery in front of bootstrap will ensure that jquery assets will be injected before bootstrap injections.

A last word to the order concerns the @cc-external packages. Keep in mind that @cc-external packages will be loaded always first.

Recursively include further externals

Now that you have written your library and you want to publish it, you should include the webpack.externals.js in you package. Because this will simplify some steps for the developer using your library.

The developer could install your library with npm i your_library. Because your library have all the external modules listed as dependencies they will be automatically installed, too. The developer have just to install this plugin and point in the webpack.externals.js to your library like this:

module.exports = [
  {
    module: "your_library",
    entries: ["dist/your_library.js"],
  },
];

Now all the externals you have defined in your own webpack.externals.js will be automatically merged into his configuration. Your externals will be placed in front of his externals.

If the developer is using a same module in his configuration, the keys entries and copy will be merged and deduplicated.

This principle will work recursively. That means that the developer using your library himself can include his own webpack.externals.js pointing to your library and publish his work. The next developer have just to point to this library and your externals will also be included.

You can stop including externals of modules by setting includeExternals: false:

module.exports = [
  {
    module: "your_library",
    entries: ["dist/your_library.js"],
    includeExternals: false,
  },
];

Walkthrough Example

Here is an example to define jQuery as an external library:

module.exports = [
  {
    module: "jquery",
    global: "jQuery",
    entries: "dist/jquery.min.js",
  },
];

This configuration will:

  1. Exclude the module jquery from the build
  2. Define the global variable jQuery as the source for this library
  3. Copy the file dist/jquery.min.js from the node_modules folder of jQuery to a subdirectory of your dist folder named vendor/jquery-X.X.X/dist/jquery.min.js (the current version will be read from the current installed package)
  4. Copy the corresponding source map (if exists of course) dist/jquery.min.js.map also to the destination folder
  5. Inject a link like <script src="vendor/jquery/dist/jquery.min.js"> to your HTML files processed by the html-webpack-plugin

Now we extend the example by adding the Bootstrap module into the configuration:

module.exports = [
  {
    module: "jquery",
    global: "jQuery",
    entries: "dist/jquery.min.js",
  },
  {
    module: "bootstrap",
    entries: ["dist/css/bootstrap.min.css", "dist/js/bootstrap.bundle.min.js"],
  },
];

As you can see, the key global is not necessary and entries can be a single string or an array of strings. All entries must end with .js or .css. All other exetensions will be ignored.

Last but not least we add the module of Font Awesome to the configuration:

module.exports = [
  {
    module: "jquery",
    global: "jQuery",
    entries: "dist/jquery.min.js",
  },
  {
    module: "bootstrap",
    entries: ["dist/css/bootstrap.min.css", "dist/js/bootstrap.bundle.min.js"],
  },
  {
    module: "@fortawesome/fontawesome-free",
    entries: "css/all.min.css",
    copy: ["webfonts/"],
  },
];

Here you see the key copy in action. This can be a string or an array of strings. All the globs defined here will be just copied to the destination folder but not injected anywhere. Here you can define single files or folders.

Switching to CDN

If you do not want to copy any files to your destination folder you can switch to CDN with just one option:

new ccWebpackExternalsPlugin({
  useCdn: true,
});

This will replace the urls in the script and the link tags with CDN targets, pointing to the exact version of the locally installed package. Here an example of using jQuery from CDN:

<script src="//cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js">

You can of course specify your own conversion function. Here is an example how to use unpkg instead of jsdelivr:

new ccWebpackExternalsPlugin({
  useCdn: true,
  getCdnPath: (name, version, path) => "//unpkg.com/${name}@${version}/${path}",
});

Thanks goes to

The basic functionality will be done by the underlying https://github.com/jharris4/html-webpack-deploy-plugin. This plugin is just an wrapper for the recursive inclusion and some simplifications and extensions.