Skip to content
This repository was archived by the owner on Apr 5, 2021. It is now read-only.

Add dist option for omitting 'dist' et al from output path #49

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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&lt;string&gt; \| object \| array&lt;object \| string&gt; | 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 `//`.<br><br>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&lt;string&gt; \| object \| array&lt;object \| string&gt; | 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 `//`.<br><br>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`<br>`  .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`<br>`  .attributes` | object.&lt;string,string&gt; | 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&lt;string&gt; \| array&lt;object&gt; | 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[]`<br>`  .path` | string | The glob path to copy assets from. | *None* |
| `externals[].supplements[]`<br>`  .dist` | string | The path of the vendor distribution directory, for this entry alone. | *Inherits from `externals[].dist`* |
| `externals[].supplements[]`<br>`  .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` |
Expand Down Expand Up @@ -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 `<output path>/vendor/bootstrap/css/bootstrap.min.css`
1. copies `node_modules/bootstrap/dist/css/bootstrap-theme.min.css` to `<output path>/vendor/bootstrap/bootstrap-theme.min.css`
1. copies all contents of `node_modules/bootstrap/dist/fonts/` to `<output path>/vendor/bootstrap/fonts/`
1. copies `node_modules/bootstrap/js/dist/dropdown.js` to `<output path>/vendor/bootstrap/dropdown.js`
1. adds `<link href="<public path>/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">` to your HTML file, before your chunks
1. adds `<link href="<public path>/vendor/bootstrap/bootstrap-theme.min.css" rel="stylesheet">` 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.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"dependencies": {
"ajv": "^6.1.1",
"copy-webpack-plugin": "^4.4.1",
"copy-webpack-plugin": "^4.6.0",
"html-webpack-include-assets-plugin": "^1.0.2"
}
}
28 changes: 20 additions & 8 deletions src/HtmlWebpackExternalsPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import CopyWebpackPlugin from 'copy-webpack-plugin'
import HtmlWebpackIncludeAssetsPlugin from 'html-webpack-include-assets-plugin'
import Ajv from 'ajv'
import configSchema from './configSchema.json'
import path from 'path'

export default class HtmlWebpackExternalsPlugin {
static validateArguments = (() => {
Expand Down Expand Up @@ -33,7 +34,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 = []
Expand All @@ -46,7 +47,7 @@ export default class HtmlWebpackExternalsPlugin {
return entry
}

const result = { ...entry, path: `${module}/${entry.path}` }
const result = { module, dist, ...entry }
localEntries.push(result)
return result
})
Expand All @@ -62,8 +63,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 }
),
]
})
Expand Down Expand Up @@ -94,11 +95,22 @@ export default class HtmlWebpackExternalsPlugin {

const pluginsToApply = []

const context = path.isAbsolute(this.cwpOptions.context)
? this.cwpOptions.context
: path.resolve(compiler.options.context, this.cwpOptions.context || ".")

pluginsToApply.push(
new CopyWebpackPlugin(
this.assetsToCopy.map(({ path, cwpPatternConfig }) => ({
from: path,
to: `${this.outputPath}/${path}`,
this.assetsToCopy.map(({ dist, cwpPatternConfig, ...entry }) => ({
dist: dist ? `/${dist}` : '',
cwpPatternConfig: cwpPatternConfig || {},
...entry
})).map(({ module, path, dist, cwpPatternConfig }) => ({
from: cwpPatternConfig.fromArgs
? {...cwpPatternConfig.fromArgs, glob:`${module}${dist}/${path}`}
: `${module}${dist}/${path}`,
to: 'UNEXPECTED_ERROR/', // We're going to be overwriting this below
transformPath: (_,absolutePath) => (absolutePath.replace(`${cwpPatternConfig.context||context}/${module}${dist}`,`${this.outputPath}/${module}`)),
...cwpPatternConfig,
})),
this.cwpOptions
Expand All @@ -115,7 +127,7 @@ export default class HtmlWebpackExternalsPlugin {
? asset
: {
...asset,
path: `${publicPath}${this.outputPath}/${asset.path}`,
path: `${publicPath}${this.outputPath}/${asset.module}/${asset.path}`,
}
),
append,
Expand Down
98 changes: 98 additions & 0 deletions test/HtmlWebpackExternalsPlugin.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -360,6 +402,62 @@ describe('HtmlWebpackExternalsPlugin', function() {
.then(() => checkHtmlIncludes('vendor/context_test/dist/contextTest.css', 'css'))
})

it('works with glob filename supplements, matching single', function() {
const externals = [
{
module: 'bootstrap',
entry: ['dist/js/bootstrap.min.js'],
supplements: [
'dist/css/bootstrap.min.c*s',
]
},
]

return runWebpack(
new HtmlWebpackPlugin(),
new HtmlWebpackExternalsPlugin({ externals })
)
.then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
})

it('works with glob filename supplements, matching multiple', function() {
const externals = [
{
module: 'bootstrap',
entry: ['dist/js/bootstrap.min.js'],
supplements: [
'dist/css/*.min.css',
]
},
]

return runWebpack(
new HtmlWebpackPlugin(),
new HtmlWebpackExternalsPlugin({ externals })
)
.then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
.then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap-reboot.min.css'))
})

it('works with recursive glob supplements', function() {
const externals = [
{
module: 'bootstrap',
entry: ['dist/js/bootstrap.min.js'],
supplements: [
'**/dropdown.js',
]
},
]

return runWebpack(
new HtmlWebpackPlugin(),
new HtmlWebpackExternalsPlugin({ externals })
)
.then(() => checkCopied('vendor/bootstrap/js/dist/dropdown.js'))
.then(() => checkCopied('vendor/bootstrap/js/src/dropdown.js'))
})

it('Specifying which HTML files to affect example', function() {
return runWebpack(
new HtmlWebpackPlugin({
Expand Down