From 72abb88f806827e5c6ca929c1880304a69f95c6e Mon Sep 17 00:00:00 2001 From: Danielle Church Date: Fri, 11 Oct 2019 22:14:34 -0400 Subject: [PATCH] Add dist option for omitting 'dist' et al from output path --- README.md | 45 ++++++++++++++++++++++++- src/HtmlWebpackExternalsPlugin.js | 16 ++++----- test/HtmlWebpackExternalsPlugin.spec.js | 42 +++++++++++++++++++++++ 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9787031..b56932e 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,17 @@ The constructor takes a configuration object with the following properties. | --- | --- | --- | --- | | `externals` | array<object> | An array of vendor modules that will be excluded from your Webpack bundle and added as `script` or `link` tags in your HTML output. | *None* | | `externals[].module` | string | The name of the vendor module. This should match the package name, e.g. if you are writing `import React from 'react'`, this would be `react`. | *None* | -| `externals[].entry` | string \| array<string> \| object \| array<object \| string> | The path, relative to the vendor module directory, to its pre-bundled distro file. e.g. for React, use `dist/react.js`, since the file exists at `node_modules/react/dist/react.js`. Specify an array if there are multiple CSS/JS files to inject. To use a CDN instead, simply use a fully qualified URL beginning with `http://`, `https://`, or `//`.

For entries whose type (JS or CSS) cannot be inferred by file extension, pass an object such as `{ path: 'https://some/url', type: 'css' }` (or `type: 'js'`). | *None* | +| `externals[].dist` | string | The relative path of the vendor distribution directory. All entry paths are resolved relative to this directory, but it is *not* included in the output path. | *None* | +| `externals[].entry` | string \| array<string> \| object \| array<object \| string> | The path, relative to the vendor distribution directory, to its pre-bundled distro file. e.g. for React, use `dist/react.js` (or, if you have set `.dist: "dist"`, just use `react.js`), since the file exists at `node_modules/react/dist/react.js`. Specify an array if there are multiple CSS/JS files to inject. To use a CDN instead, simply use a fully qualified URL beginning with `http://`, `https://`, or `//`.

For entries whose type (JS or CSS) cannot be inferred by file extension, pass an object such as `{ path: 'https://some/url', type: 'css' }` (or `type: 'js'`). | *None* | | `externals[].entry.path` | string | If entry is an object, the path to the asset. | *None* | +| `externals[].entry.dist` | string | The path of the vendor distribution directory, for this entry alone. | *Inherits from `externals[].dist`* | | `externals[].entry.type` | `'js'`\|`'css'` | The asset type, if it cannot be inferred. | *Inferred by extension when possible* | | `externals[].entry`
`  .cwpPatternConfig` | object | The properties to set on the [`copy-webpack-plugin` pattern object](https://github.com/webpack-contrib/copy-webpack-plugin#patterns). This object is merged in with the default `from` and `to` properties which are generated by the externals plugin. | `{}` | | `externals[].entry`
`  .attributes` | object.<string,string> | Additional attributes to add to the injected tag. | `{}` | | `externals[].global` | string \| null | For JavaScript modules, this is the name of the object globally exported by the vendor's dist file. e.g. for React, use `React`, since `react.js` creates a `window.React` global. For modules without an export (such as CSS), omit this property or use `null`. | `null` | | `externals[].supplements` | array<string> \| array<object> | For modules that require additional resources, specify globs of files to copy over to the output. e.g. for Bootstrap CSS, use `['dist/fonts/']`, since Glyphicon fonts are referenced in the CSS and exist at `node_modules/bootstrap/dist/fonts/`. Supplements can be specified as just an array of paths, or an array of objects with a path and copy plugin pattern object. | `[]` | | `externals[].supplements[]`
`  .path` | string | The glob path to copy assets from. | *None* | +| `externals[].supplements[]`
`  .dist` | string | The path of the vendor distribution directory, for this entry alone. | *Inherits from `externals[].dist`* | | `externals[].supplements[]`
`  .cwpPatternConfig` | object | The properties to set on the [`copy-webpack-plugin` pattern object](https://github.com/webpack-contrib/copy-webpack-plugin#patterns). This object is merged in with the default `from` and `to` properties which are generated by the externals plugin. | `{}` | | `externals[].append` | boolean | Set to true to inject this module after your Webpack bundles. | `false` | | `hash` | boolean | Set to true to append the injected module distro paths with a unique hash for cache-busting. | `false` | @@ -250,6 +253,46 @@ new HtmlWebpackExternalsPlugin({ }) ``` +### Omitting distribution path example + +By default, the Webpack output directory includes the full relative path of all copied files relative to their module root, including top-level directories like `dist` or `build`. You can omit these top-level directories in your output. + +Do not include a trailing slash or leading slash in the distribution path, they are concatenated automatically by the plugin. + +This example assumes `bootstrap` is installed in the app. It: + +1. copies `node_modules/bootstrap/dist/css/bootstrap.min.css` to `/vendor/bootstrap/css/bootstrap.min.css` +1. copies `node_modules/bootstrap/dist/css/bootstrap-theme.min.css` to `/vendor/bootstrap/bootstrap-theme.min.css` +1. copies all contents of `node_modules/bootstrap/dist/fonts/` to `/vendor/bootstrap/fonts/` +1. copies `node_modules/bootstrap/js/dist/dropdown.js` to `/vendor/bootstrap/dropdown.js` +1. adds `` to your HTML file, before your chunks +1. adds `` to your HTML file, before your chunks + +```js +new HtmlWebpackExternalsPlugin({ + externals: [ + { + module: 'bootstrap', + dist: 'dist', + entry: [ + 'css/bootstrap.min.css', + { + dist: 'dist/css', + path: 'bootstrap-theme.min.css', + }, + ], + supplements: [ + 'fonts/', + { + dist: 'js/dist', + path: 'dropdown.js', + }, + ], + }, + ], +}) +``` + ### Customizing public path example By default, local externals are resolved from the same root path as your Webpack configuration file's `output.publicPath`, concatenated with the `outputPath` variable. This is configurable. diff --git a/src/HtmlWebpackExternalsPlugin.js b/src/HtmlWebpackExternalsPlugin.js index 95dad13..363df66 100644 --- a/src/HtmlWebpackExternalsPlugin.js +++ b/src/HtmlWebpackExternalsPlugin.js @@ -33,7 +33,7 @@ export default class HtmlWebpackExternalsPlugin { this.enabled = enabled this.cwpOptions = cwpOptions - externals.forEach(({ module, entry, global, supplements, append }) => { + externals.forEach(({ module, dist, entry, global, supplements, append }) => { this.externals.push(global ? { [module]: global } : module) const localEntries = [] @@ -46,7 +46,7 @@ export default class HtmlWebpackExternalsPlugin { return entry } - const result = { ...entry, path: `${module}/${entry.path}` } + const result = { module, dist, ...entry } localEntries.push(result) return result }) @@ -62,8 +62,8 @@ export default class HtmlWebpackExternalsPlugin { ...localEntries, ...supplements.map(asset => typeof asset === 'string' - ? { path: `${module}/${asset}`, cwpPatternConfig: {} } - : { ...asset, path: `${module}/${asset.path}` } + ? { module, dist, path: asset, cwpPatternConfig: {} } + : { module, dist, ...asset } ), ] }) @@ -96,9 +96,9 @@ export default class HtmlWebpackExternalsPlugin { pluginsToApply.push( new CopyWebpackPlugin( - this.assetsToCopy.map(({ path, cwpPatternConfig }) => ({ - from: path, - to: `${this.outputPath}/${path}`, + this.assetsToCopy.map(({ module, path, dist, cwpPatternConfig }) => ({ + from: `${module}/${dist?`${dist}/`:''}${path}`, + to: `${this.outputPath}/${module}/${path}`, ...cwpPatternConfig, })), this.cwpOptions @@ -115,7 +115,7 @@ export default class HtmlWebpackExternalsPlugin { ? asset : { ...asset, - path: `${publicPath}${this.outputPath}/${asset.path}`, + path: `${publicPath}${this.outputPath}/${asset.module}/${asset.path}`, } ), append, diff --git a/test/HtmlWebpackExternalsPlugin.spec.js b/test/HtmlWebpackExternalsPlugin.spec.js index 28bd273..8c22a78 100644 --- a/test/HtmlWebpackExternalsPlugin.spec.js +++ b/test/HtmlWebpackExternalsPlugin.spec.js @@ -234,6 +234,48 @@ describe('HtmlWebpackExternalsPlugin', function() { ) }) + it('Omitting distribution path example', function() { + return runWebpack( + new HtmlWebpackPlugin(), + new HtmlWebpackExternalsPlugin({ + externals: [ + { + module: 'bootstrap', + dist: 'dist', + entry: [ + 'css/bootstrap.min.css', + { + dist: 'dist/css', + path: 'bootstrap-reboot.min.css', + }, + ], + supplements: [ + 'js/', + { + dist: 'js/dist', + path: 'dropdown.js', + }, + ], + }, + ], + }) + ) + .then(() => checkCopied('vendor/bootstrap/css/bootstrap.min.css')) + .then(() => checkCopied('vendor/bootstrap/bootstrap-reboot.min.css')) + .then(() => checkCopied('vendor/bootstrap/js/bootstrap.bundle.js')) + .then(() => checkCopied('vendor/bootstrap/dropdown.js')) + .then(() => + checkHtmlIncludes( + 'vendor/bootstrap/css/bootstrap.min.css', + 'css' + )) + .then(() => + checkHtmlIncludes( + 'vendor/bootstrap/bootstrap-reboot.min.css', + 'css' + )) + }) + it('Customizing public path example', function() { return runWebpack( new HtmlWebpackPlugin(),