GithubHelp home page GithubHelp logo

numical / script-ext-html-webpack-plugin Goto Github PK

View Code? Open in Web Editor NEW
588.0 588.0 105.0 337 KB

Enhances html-webpack-plugin functionality with different deployment options for your scripts including 'async', 'preload', 'prefetch', 'defer', 'module', custom attributes, and inlining.

License: MIT License

JavaScript 100.00%

script-ext-html-webpack-plugin's Introduction

Script Extension for HTML Webpack Plugin

npm version Dependency Status Build status js-semistandard-style

NPM

Deprecation Warning

tl;dr
This project is no longer maintained. It does not support Webpack 5.

A bit more detail
Any look at the project activity will show that I have not been able to maintain this project adequately.
The advent of version 5 of Webpack requires another bout of refactoring that I simply have no time for.
Consequently v2.15.0 will be the last version of this plugin. My thanks to all users, and especially to all contributors, of this plugin over the years.
My apologies to all those whose webpack 5 migration has been made more complicated by this decision.

But I still want to use the plugin...
Feel free!
My last update works with versions of v4.44.2 of webpack and v4.5.0 of html-webpack-plugin.
Forkers feel free! That's what the licence is for.
In fact, if you fork with an intention to support on-going development, let me know! I'll happily link to your repository here and offer some tips (main one: ditch backward compatibility - it's a pain).
I will formally archive this repository at the end of the 2020.

Summary

Enhances html-webpack-plugin functionality with different deployment options for your scripts including:

This is an extension plugin for the webpack plugin html-webpack-plugin - a plugin that simplifies the creation of HTML files to serve your webpack bundles.

The raw html-webpack-plugin incorporates all webpack-generated javascipt as synchronous<script> elements in the generated html. This plugin allows you to:

  • add standard and custom attributes to these elements;
  • inline the code in the elements;
  • add prefetch and preload resource hints for initial and dynamically loaded scripts.

Installation

You must be running webpack (1.x, 2.x, 3.x, 4.x) on node 6+. Install the plugin with npm:

$ npm install --save-dev script-ext-html-webpack-plugin

Not that you will need v3.0.6+ or v4.x of html-webpack-plugin

For those requiring earlier versions of node, please use the last 1.x version of this plugin. However please note this does not have webpack 4.x support:

$ npm install --save-dev [email protected]

You may see an UNMET PEER DEPENDENCY warnings for webpack and various plugins.

This is fine; in testing, we dynamically download multiple versions of webpack (via the dynavers module).

Basic Usage

Add the plugin to your webpack config as follows:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin()
]  

The order is important - the plugin must come after HtmlWebpackPlugin.

The above configuration will actually do nothing due to the configuration defaults.

Some more useful scenarios:

All scripts set to async:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    defaultAttribute: 'async'
  })
]  

All scripts set to async except 'first.js' which is sync:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    sync: 'first.js',
    defaultAttribute: 'async'
  })
]  

Configuration offers much more complex options:

Configuration

You must pass a hash of configuration options to the plugin to cause the addition of attributes:

  • inline: a script matching pattern defining scripts that should be inlined in the html (default: []);
  • sync: a script matching pattern defining script names that should have no attribute (default: []);
  • async: a script matching pattern defining script names that should have an async attribute (default: []);
  • defer: a script matching pattern defining script names that should have a defer attribute (default: []);
  • defaultAttribute: 'sync' | 'async' | 'defer' The default attribute to set - 'sync' actually results in no attribute (default: 'sync');
  • module: a script matching pattern defining script names that should have a type="module" attribute (default: []);
  • preload: a script matching pattern defining scripts that should have accompanying preload resource hints (default: []);
  • prefetch: a script matching pattern defining scripts that should have accompanying prefetch resource hints (default: []);
  • custom: a single hash or an array of hashes with the following structure:
    • test: a script matching pattern defining scripts that should have a custom attribute added;
    • attribute: a String attribute to add;
    • value: (optional) a String value for the attribute; if not set the attribute has no value set (equivalent of true).

A script matching pattern matches against a script's name. It can be one of:

  • a String- matches if it is a substring of the script name;
  • a RegExp;
  • an array of String's and/or RegExp's - matches if any one element matches;
  • a hash with property test with a value of one of the above.

In more complicated use cases it may prove difficult to ensure that the pattern matching for different attributes are mutually exclusive. To prevent confusion, the plugin operates a simple precedence model:

  1. if a script name matches theinline script matching pattern, it will be inlined;

  2. if a script name matches the sync script matching pattern, it will have no attribute, unless it matched condition 1;

  3. if a script name the async script matching pattern, it will have the async attribute, unless it matched conditions 1 or 2;

  4. if a script name matches the defer script matching pattern, it will have the defer attribute, unless it matched conditions 1, 2 or 3;

  5. if a script name does not match any of the previous conditions, it will have the `defaultAttribute' attribute.

The module attribute is independent of conditions 2-5, but will be ignored if the script isinlined.

Dynamically Loaded Scripts

The preload and prefetch configuration also have allow an additional property in the hash form that can be passed to include dynamically loaded (asynchronous) scripts. This property is chunks and can have one of the following String values:

  • initial: default behaviour, no asynchronour scripts;
  • async: only asynchronouse scripts;
  • all: all scripts Note that you must still supply a test script matching pattern which is also applied when selecting scripts.

Configuration Examples

All scripts with 'important' in their name are sync and all others set to defer:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    sync: 'important',
    defaultAttribute: 'defer'
  })
]  

Alternatively, using a regular expression:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    sync: /important/,
    defaultAttribute: 'defer'
  })
]  

All scripts with 'mod' in their name are async and type 'module', all others are sync (no explicit setting for this as it is the default):

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    async: 'mod',
    module: 'mod'
  })
]  

Script 'startup.js' is inlined whilst all other scripts are async and preloaded:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    inline: 'startup',
    preload: /\.js$/,
    defaultAttribute: 'async'
  })
]  

All scripts are preloaded with a crossorigin attribute set to enable CDN's:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    custom: {
      test: /\.js$/,
      attribute: 'crossorigin',
      value: 'anonymous'
    },
    preload: {
      test: /\.js$/
    }
  })
]  

All asynchronous scripts are added as preload resource hints. All other scripts are async:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    async: /\.js$/,
    preload: {
      test: /\.js$/,
      chunks: 'async'
    }
  })
]  

All scripts have custom attribute type='text/paperscript' and ui.js also has a custom attribute of id='1235':

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    custom: [
      {
        test: /\.js$/,
        attribute: 'type',
        value: 'text/paperscript'
      },
      {
        test: 'ui.js',
        attribute: 'id',
        value: '12345'
      }
    ]
  })
]  

And so on, to craziness:

plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    inline: 'startup',  
    sync: [/imp(1|2){1,3}}/, 'initial'],
    defer: ['slow', /big.*andslow/],
    module: [/^((?!sync).)*/, 'mod'],
    prefetch: 'indirectly-referenced.js',
    defaultAttribute: 'async'
  })
]  

Any problems with real-world examples, just raise an issue.

A Note on Script Names

In the above examples the actual script names are used to select the deployment option. You may not wish to couple asset names to your deployment like this. Instead you can use Webpack's entry configuration to create aliases that the plugin will then use for its pattern matching. Your webpack.config.js will look something like this:

entry: {
  a: path.join(__dirname, 'lib/myFunctions.js'),
  b: path.join(__dirname, 'lib/otherFunctions.js'),
  c: path.join(__dirname, 'lib/criticalFuntions.js')
},
output: {
  ...
  filename: '[name].js'
}
plugins: [
  new HtmlWebpackPlugin(),
  new ScriptExtHtmlWebpackPlugin({
    inline: ['c'],  
    defer: ['a', 'b']
  })
]  

Inlining

Several notes and caveats apply:

  • This feature is for <script>'s only. If you wish to inline css please see the sister plugin style-ext-html-webpack-plugin.
  • Even the simplest script will be wrapped with webpack boilerplate; ensure you minify your javascript if you want your output html to be legible!
  • Hot replacement of inlined scripts will only work if caching is switched off for html-webpack-plugin:
plugins: [
    new HtmlWebpackPlugin({
      cache: false
    }),
    new ScriptExtHtmlWebpackPlugin({
      inline: ['myinlinedscript.js']
    })
]

Resource Hints

In most cases, modern browsers will intelligently preload referenced script assets. However if you wish, this plugin can add resource hint elements to the <head> element of the form:

<link rel="[preload|prefetch]" href="[scriptname]" as="script">

Use the preload and prefetch configuration options. Where preload and prefetch patterns overlap, preload takes precedence.

Possibly a more compelling use case is to preload/prefetch dynamically loaded scripts generated by Webpack's code splitting. Since v1.7.0, this plugin can do this - see 'Dynamically Loaded Scripts' above.

Notes:

  • custom attributes will be added to resource hints with the same script matching pattern. This is useful for adding such attributes as crossorigin="anonymous" - see the Configuration Examples above;
  • for more on resource hints, see the w3c definition;
  • for a more complete solution that allows the preloading\fetching of assets other than scripts, see the resource-hints-webpack-plugin.

Change History

v2.1.5

  • end of life version
  • updated all dependencies
  • fixes some tests to accomodate change in html-webpack-plugin output
  • added end-of-life section to README.

v2.1.x

  • support for changes in html-webpack-plugin 4.x since alpha and beta
  • custom attributes now added to resource hints too (see pull request 53 for discussion)
  • update dependencies

v2.0.x

  • support html-webpack-plugin 4.x - huge thanks to @snadn
  • support webpack 4.x - huge thanks to @sherlock1982
  • node 9.x 10,x, 11.x testing
  • remove support for node 4.x and 5.x
  • remove Appveyor config
  • temporary remove Handlebars test until loader supports webpack 4.x

v1.8.x

  • added custom attributes - now works on inline scripts as well e.g. for CSP nonces, -thanks @niieani and @phallguy
  • compatible with webpack-config - thanks @avaly
  • node v8+ and webback 3.x testing
  • resource hints handle public paths without end separators - thanks @albv
  • updated dependencies (including dev and peer) - thanks @ai, @malikshahzad228
  • windows-proofed public paths - thanks @mstijak, @Jesseyx
  • added appveyor support for windows build and testing - CURRENTLY SWITCHED OFF

v1.7.x

  • updated for Webpack 2.5.x and updated all dependencies
  • adds asynchronous script resource hints
  • fixed issue 13 - inline functionality not working with HtmlWebpackPlugin hashing
  • fixed issue 16 - unnecessary closing tag
  • fixed issue 18 - added defensive coding against unpopulated event arguments
  • refactored for better handling of publicPath - thanks @koalaink

v1.6.x

  • works with webpack 2.2.1
  • enhanced API (no need to use array), fully backwardly compatible
  • refactor in preparation for v2

v1.5.x

  • added resource hints
  • works with webpack 2.2.0

v1.4.x

v1.3.x

  • added type="text/javascript" by default, in response to Safari 9.1.1 bug
  • removed experimental status of inline option
  • added weback 2.2.x beta support

v1.2.x

  • added inline option

v1.1.x

  • added type="module" option

v1.0.x

  • initial release

script-ext-html-webpack-plugin's People

Contributors

avaly avatar dependabot[bot] avatar dlhandsome avatar jtiala avatar numical avatar snadn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

script-ext-html-webpack-plugin's Issues

Script neither src nor inline

In my use case i want to render a script tag inside {{#if /if}} as is:

{{#if htmlWebpackPlugin.options.metadata.isDevServer}}
  <script>window.initial_state = {{{ htmlWebpackPlugin.options.INITIAL_STATE }}}</script>
  {{else}}
  <script>window.initial_state = <%= {:safe, initial_state} %></script>
  {{/if}}

This throws the error Cannot read property '1' of null.
As far as i can tell, replaceScriptElements() expects each script tag to either have a src tag or be in the inline options.
I fixed it like this but you probably want to do it more elegantly:

var scriptName = SRC_PATTERN.exec(scriptElement);
    if(scriptName) {
      scriptName = scriptName[1]; 
      if (matches(scriptName, options[INLINE])) {
        return generateInlineScriptElement(scriptName, compilation);
      } else {
        return generateSrcScriptElement(options, scriptName);
      }
    } else {
      return scriptElement;

Thank you for your work on the project!

Inline script is never output

I'm attempting to add an inlined script to my output that lives in the same directory as my webpack config:

// test.js

console.log('Hello, from WebPack');


WebPack Configuration:

plugins: [
  . . .

  new HtmlWebpackPlugin({
    cache: false,
    template: 'src/index.html',
    title: METADATA.title,
    chunksSortMode: 'dependency',
    metadata: METADATA,
    inject: 'head'
  }),

  new ScriptExtHtmlWebpackPlugin({
    inline: ['test.js']
  }),

]

However, I never see a console statement. What am I doing wrong?

Support templates

It seems like this plugin does nothing when a template is used in HTMLWebpackPlugin. It would be useful, if possible, for this plugin to add the attributes to the htmlWebpackPlugin variable that gets passed to the template somehow (in the files field or a separate field).

ScriptExtHtmlWebpackPlugin: no asset with href

Hello, after upgrade to 1.8.3 version of this plugin i get this error:

ERROR in Error: ScriptExtHtmlWebpackPlugin: no asset with href '/dist/inline-preboot.js'

  • elements.js:49 replaceWithInlineElement
    [universal-demo]/[script-ext-html-webpack-plugin]/lib/elements.js:49:21

  • elements.js:39 updateScriptElement
    [universal-demo]/[script-ext-html-webpack-plugin]/lib/elements.js:39:7

  • elements.js:30 updateElement
    [universal-demo]/[script-ext-html-webpack-plugin]/lib/elements.js:30:7

  • Array.map

  • elements.js:25 Object.update
    [universal-demo]/[script-ext-html-webpack-plugin]/lib/elements.js:25:15

  • plugin.js:44 Compilation.compilation.plugin
    [universal-demo]/[script-ext-html-webpack-plugin]/lib/plugin.js:44:40

  • Tapable.js:208 Compilation.applyPluginsAsyncWaterfall
    [universal-demo]/[tapable]/lib/Tapable.js:208:13

  • util.js:16 Compilation.tryCatcher
    [universal-demo]/[bluebird]/js/release/util.js:16:23

  • index.js:641
    [universal-demo]/[html-webpack-plugin]/index.js:641:12

  • index.js:151
    [universal-demo]/[html-webpack-plugin]/index.js:151:16

  • util.js:16 tryCatcher
    [universal-demo]/[bluebird]/js/release/util.js:16:23

  • promise.js:512 Promise._settlePromiseFromHandler
    [universal-demo]/[bluebird]/js/release/promise.js:512:31

  • promise.js:569 Promise._settlePromise
    [universal-demo]/[bluebird]/js/release/promise.js:569:18

  • promise.js:614 Promise._settlePromise0
    [universal-demo]/[bluebird]/js/release/promise.js:614:10

  • promise.js:693 Promise._settlePromises
    [universal-demo]/[bluebird]/js/release/promise.js:693:18

  • async.js:133 Async._drainQueue
    [universal-demo]/[bluebird]/js/release/async.js:133:16

  • async.js:143 Async._drainQueues
    [universal-demo]/[bluebird]/js/release/async.js:143:10

  • async.js:17 Immediate.Async.drainQueues
    [universal-demo]/[bluebird]/js/release/async.js:17:14

With previous version 1.8.2 it is working. Problem is occurs when you use this plugin in combination with virtual-module-webpack-plugin.

Here is webpack.config.js which is failing:
https://github.com/kukjevov/ng-universal-demo/blob/ad9fe9a5baa3bfe7c7628dfb15fb76b016d2bce6/webpack.config.js

UPDATE
It looks like that this is general problem of this version, not working even when using file, not virtual-module-webpack-plugin

Multiple Webpack outputs not generating correct script tags

I'm trying to write a webpack configuration that will output this sort of output to my index.html:

<script src="main.legacy.js" nomodule></script>
<script type="module" src="main.mjs"></script>

But it's not working as expected. Here are the files I'm using:

webpack.config.js

const path = require('path')

const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')

module.exports = () => {

	const entry = ['./src']

	const plugins = [
		new ExtractTextPlugin({ filename: '[name].css', allChunks: true }),
		new HtmlWebpackPlugin({ template: './src/index.html' }),
    // This seems to be correct... but maybe I'm missing something
		new ScriptExtHtmlWebpackPlugin({
			module: /\.mjs$/,
			custom: [
				{
					test: /\.js$/,
					attribute: 'nomodule',
				}
			]
		})
	]

	const generateModule = esmodules => ({
		rules: [
			{
				test: /\.css$/,
				use: ExtractTextPlugin.extract({
					fallback: 'style-loader',
					use: 'css-loader'
				})
			},
			{
				test: /\.js$/,
				exclude: /node_modules/,
				loader: 'babel-loader',
				options: {
					presets: [
						['@babel/preset-env', {
							useBuiltIns: 'usage',
							corejs: '3.0.0',
							targets: {
								esmodules: !!esmodules
							}
						}]
					]
				}
			}
		]
	})

	const legacyConfig = {
		entry,
		output: {
			path: path.resolve(__dirname, 'public'),
			filename: '[name].legacy.js',
		},
		module: generateModule(false),
		plugins
	}

	const modernConfig = {
		entry,
		output: {
			path: path.resolve(__dirname, 'public'),
			filename: '[name].mjs',
		},
		module: generateModule(true),
		plugins
	}

	return [legacyConfig, modernConfig]
}

src/index.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Some title</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
	</head>
	<body>
		<!-- Some required element in my project -->
		<div id="header"></div>
	</body>
</html>

package.json

{
  "name": "some-project",
  "version": "1.0.0",
  "private": true,
  "description": "",
  "scripts": {
    "build": "webpack"
  },
  "dependencies": {
    "@babel/core": "^7.7.7",
    "@babel/polyfill": "^7.7.0",
    "@babel/preset-env": "^7.7.7",
    "babel-loader": "^8.0.6",
    "core-js": "^3.6.1",
    "css-loader": "^3.4.1",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "html-webpack-plugin": "^3.2.0",
    "http-server": "^0.12.0",
    "script-ext-html-webpack-plugin": "^2.1.4",
    "style-loader": "^1.1.2",
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.1"
  }
}

When I run npm run build, the javascript files are bundled as expected and exist in the filesystem under /public, but the following /public/index.html is outputted:

Expected /public/index.html output

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Some title</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
	<link href="main.css" rel="stylesheet"></head>
	<body>
		<!-- Some required element in my project -->
		<div id="header"></div>

    <!-- Both legacy and modern module script tags are present  -->
    <script src="main.legacy.js" nomodule></script>
	  <script type="module" src="main.mjs"></script>
  </body>
</html>

Actual /public/index.html output

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Some title</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
	<link href="main.css" rel="stylesheet"></head>
	<body>
		<!-- Some required element in my project -->
		<div id="header"></div>
	<script type="module" src="main.mjs"></script></body>
</html>

Only the one modern module script is present. I also tried using the following config as well:

new ScriptExtHtmlWebpackPlugin({
			custom: [
				{
					test: /\.mjs$/,
					attribute: 'type',
					value: 'module'
				},
				{
					test: /\.js$/,
					attribute: 'nomodule',
				}
			]
})

I got the exact same results. Am I doing something wrong? Is this a bug?

ability to move inlined scripts to the head tag

Hi πŸ˜„

I want to move scripts which are inlined, to the top of the HEAD tag. I patched the plugin to do this, however, would it be fine if I made a Pull Request for this feature? This basically allows us to leverage the default html-webpack-plugin template and still reorder the scripts.

Possible implementation:

// elements.js replaceWithInlineElement method
...  
if(options.inlineAtHead){
  newtag.__mark_inlined__ = true;
}
...
// plugin.js compilation.plugin handler

if(options.inlineAtHead){
  // elements.update marks inlined script tags
  const moveToHead = pluginArgs.body.filter((tag) => {
    return tag.tagName === 'script' && tag.__mark_inlined__;
  });
  pluginArgs.body = pluginArgs.body.filter((tag) => {
    return !(tag.tagName === 'script' && tag.__mark_inlined__);
  });
  moveToHead.forEach((tag) => {
    delete tag.__mark_inlined__;
  });
 // can inline at top of head or bottom of head. (e.g, need to do something immediately or not) 
  if(options.inlineAtHead === 'top'){
    pluginArgs.head = [...moveToHead, ...pluginArgs.head];
  }else{
    pluginArgs.head = [...pluginArgs.head, ...moveToHead];
  }

actual usage in webpack config

[
...
    new ScriptExtHtmlWebpackPlugin({
      inline: ['detect-browser'],
      inlineAtHead: 'top'
    }),
...
]

Thank you!

Multiple pages with 'inline' cause error: 'no asset with href'

Hi, i have an application with multiple html pages, i want embed some js code (bootload.js) into html pages, so use the 'inline' option, like this:

module.exports = {
    entry: {
        'bootload': './src/bootload.js',
        'home': './src/home.js',
        'thirdpart': ['jquery', 'lodash'],
        'libs': libFiles.map( v => `./src/libs/${v.path}` ),
        // ... pages
    },
    output: {
        path: path.resolve(__dirname, '../dist'),
        filename: 'js/[name]-[hash].js',
    },
    plugins: [
        new HtmlWebpackPlugin({
            chunks: [ 'bootload', 'thirdpart', 'home' ],
            template: './src/index.html',
            filename: 'index.html'
        }),
        new HtmlWebpackPlugin({
            chunks: [ 'bootload', 'thirdpart', 'libs' /*, pages...*/ ],
            template: './src/sub-page.html',
            filename: 'sub-page.html'
        }),
        new ScriptExtHtmlWebpackPlugin({
            inline: ['bootload']
        }),
    ]
};

but, this caused error 'no asset with href':

Hash: 79052c98769190853a0b
Version: webpack 4.28.0
Time: 1363ms
Built at: 2018-12-25 14:33:47
                               Asset       Size     Chunks             Chunk Names
                          index.html   12.3 KiB             [emitted]
     js/home-79052c98769190853a0b.js    790 KiB       home  [emitted]  home
     js/libs-79052c98769190853a0b.js   9.76 KiB       libs  [emitted]  libs
    js/page1-79052c98769190853a0b.js    744 KiB      page1  [emitted]  page1
    js/page2-79052c98769190853a0b.js   9.47 KiB      page2  [emitted]  page2
js/thirdpart-79052c98769190853a0b.js   2.09 MiB  thirdpart  [emitted]  thirdpart
              pages/page1\index.html  793 bytes             [emitted]
              pages/page2\index.html  620 bytes             [emitted]
Entrypoint bootload = js/bootload-79052c98769190853a0b.js
Entrypoint home = js/home-79052c98769190853a0b.js
Entrypoint thirdpart = js/thirdpart-79052c98769190853a0b.js
Entrypoint libs = js/libs-79052c98769190853a0b.js
Entrypoint page1 = js/page1-79052c98769190853a0b.js
Entrypoint page2 = js/page2-79052c98769190853a0b.js
[0] multi jquery lodash 40 bytes {thirdpart} [built]
[1] multi ./src/libs/lib1/index.js ./src/libs/lib2/index.js 40 bytes {libs} [built]
[./node_modules/css-loader/dist/cjs.js!./src/styles/main.css] 240 bytes {home} [built]
[./src/bootload.js] 836 bytes {bootload} [built]
[./src/home.js] 317 bytes {home} [built]
[./src/libs/lib1/index.js] 83 bytes {libs} {page1} [built]
[./src/libs/lib2/index.js] 65 bytes {libs} {page2} [built]
[./src/mods/mod2 sync recursive ^\.\/.*$] ./src/mods/mod2 sync ^\.\/.*$ 247 bytes {home} [built]
[./src/mods/mod2/mod2-1.js] 52 bytes {home} [optional] [built]
[./src/mods/mod2/mod2-2.js] 52 bytes {home} [optional] [built]
[./src/pages/page1/index.js] 558 bytes {page1} [built]
[./src/pages/page1/part1.js] 194 bytes {page1} [built]
[./src/pages/page1/part2.js] 194 bytes {page1} [built]
[./src/pages/page2/index.js] 95 bytes {page2} [built]
[./src/styles/main.css] 1.06 KiB {home} [built]
    + 10 hidden modules

ERROR in ScriptExtHtmlWebpackPlugin: no asset with href '../../js/bootload-79052c98769190853a0b.js'

ERROR in ScriptExtHtmlWebpackPlugin: no asset with href '../../js/bootload-79052c98769190853a0b.js'
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [./node_modules/html-webpack-plugin/lib/loader.js!./src/index.html] 585 bytes {0} [built]
Child html-webpack-plugin for "pages\page1\index.html":
     1 asset
    Entrypoint undefined = pages/page1\index.html
    [./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/page1/index.html] 516 bytes {0} [built]
Child html-webpack-plugin for "pages\page2\index.html":
     1 asset
    Entrypoint undefined = pages/page2\index.html
    [./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/page2/index.html] 327 bytes {0} [built]

Is there some way to embed script code into multiple html pages?

Thank

Does not work for Vue.js SPA


const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
....

 plugins:      [
        new VueLoaderPlugin(),
        new FriendlyErrorsPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new ScriptExtHtmlWebpackPlugin({
            defaultAttribute: 'defer',
            preload:          /\.js$/,
        }),

 "dependencies": {
    "@fortawesome/fontawesome-free": "^5.5.0",
    "@mintuz/horizon": "BonBonSlick/horizon",
    "bootstrap": "^4.1.3",
    "cross-env": "^5.2.0",
    "dotenv": "^8.0.0",
    "eslint": "^6.8.0",
    "eslint-friendly-formatter": "^4.0.1",
    "eslint-loader": "^2.1.1",
    "eslint-plugin-html": "^5.0.0",
    "eslint-plugin-node": "^8.0.0",
    "eslint-plugin-promise": "^4.0.1",
    "faker": "^4.1.0",
    "i": "^0.3.6",
    "ismobilejs": "^1.0.3",
    "js-cookie": "^2.2.0",
    "mini-css-extract-plugin": "^0.8.0",
    "moment": "^2.22.2",
    "npm": "^6.10.2",
    "resumablejs": "^1.1.0",
    "script-ext-html-webpack-plugin": "^2.1.4",
    "secure-ls": "^1.2.5",
    "source-map-loader": "^0.2.4",
    "standard": "^14.3.1",
    "uglifyjs-webpack-plugin": "^2.1.3",
    "url-loader": "^1.1.2",
    "video.js": "^7.6.0",
    "vue": "^2.5.17",
    "vue-meta": "^2.3.1",
    "vue-resource": "^1.2.1",
    "vue-router": "^3.0.1",
    "vue-style-loader": "^4.1.0",
    "vuex": "^3.0.1",
    "vuex-persistedstate": "^2.7.0",
    "vuex-router-sync": "^5.0.0",
    "webpack-cli": "^3.3.6",
    "webpack-merge": "^4.1.4"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-eslint": "^10.0.3",
    "babel-loader": "^7.1.4",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-2": "^6.24.1",
    "copy-webpack-plugin": "^5.0.4",
    "css-loader": "^0.28.11",
    "eslint-plugin-vue": "^4.7.1",
    "file-loader": "^4.2.0",
    "friendly-errors-webpack-plugin": "^1.7.0",
    "html-webpack-plugin": "^4.0.0",
    "node-sass": "^4.10.0",
    "popper.js": "^1.12",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.13.1",
    "vue-loader": "^15.2.2",
    "vue-router-sitemap": "^0.0.4",
    "vue-template-compiler": "^2.5.17",
    "watch": "^1.0.2",
    "webpack": "^4.20.2",
    "webpack-bundle-analyzer": "^3.4.1",
    "webpack-dev-server": "^3.8.0"
  }

Result


<head>
<link rel="preload" href="/main.js" as="script"> // worked only for main.js!
<script src="/0.js" ></script> // scripts appended in head
<script src="/1.js" ></script>
....
<footer>
<script src="/main.js" defer=""></script> // only here added DEFER

Expected, everything DEFER and preload.

<head>
<link rel="preload" href="/0.js" as="script">
....
<footer>
<script src="/0.js"  defer></script>

Inline option doesn't work

When insert the option
...
inline: ['someChunk', 'manifest']
...
The script crashes:

TypeError: Cannot read property 'src' of undefined

  • common.js:10 getRawScriptName
    [frontend-dashboard]/[script-ext-html-webpack-plugin]/lib/common.js:10:47

Approve please the pullRequest 00aa0e9

Ability to move preload to the top of <head>

This is somewhat for perf geeks, but if the head is quite long (because lots of icon attrs and meta tags), it would be useful to have an option to add the preload tags right at the start of <head>.
That way the browser can preload earlier.

Duplicate entry of script files which are configured for preloading

This is my webpack configuration for ScriptExtHtmlWebpackPlugin

 new ScriptExtHtmlWebpackPlugin({
        async : [/app/],
        preload : [/manifest/,/vendor\.bundle/]
    }),

I want to preload my vendor files and asynchronously load my app build file.
But the html generated has the entry for manifest and vendor files twice, one for preload hint and the other as a script tag
screen shot 2017-05-22 at 9 01 40 am

Prior apologies if the configuration has been done wrong ).

Circular reference detected (v1.8.5)

When we add the ScriptExtHtmlWebpac plugin to our configuration, Grunt complains with this error. Here is the full output:

Warning: Circular reference detected (.webpack.dev.plugins[1].options.compilationOptions.plugins[1].options)

Here is our Webpack config:

return {
    'cache'   : true,
    'devtool' : 'cheap-module-source-map',
    'module'  : {
        'rules' : [
            {
                'test' : /language\/\w+_\w+\/.*\.json$/,
                'use'  : {
                    'loader' : 'lang-loader'
                }
            }
        ]
    },
    'resolve' : {
        'modules' : [
            oConfig.sSymlinkPath,
            oConfig.sNodeModulePath
        ],
        'alias' : oAlias
    },
    'resolveLoader' : {
        'modules' : [
            oConfig.sSymlinkPath,
            oConfig.sNodeModulePath
        ],
        'alias' : {
            'text'        : 'raw-loader',
            'lang-loader' : path.resolve(oConfig.sWebpackPath, 'lang-loader.js')
        }
    },
    'entry' : {
        'main' : './' + oConfig.sModuleName + '.js'
    },
    'output' : {
        'filename' : '[name].[chunkhash].js',
        'path'     : oConfig.sServePath
    },
    'plugins' : [
        new HtmlWebpackPlugin({
            'template' : path.resolve(oConfig.sSymlinkPath, oConfig.sAppPath, 'index.html')
        }),

        new ScriptExtHtmlWebpackPlugin({
            'defaultAttribute' : 'defer'
        }),

        new webpack.optimize.CommonsChunkPlugin({
            'name'      : 'vendor',
            'minChunks' : function(module)
            {
                // console.log(module.context);
                return /node_modules/.test(module.context) ||
                    /can\/can/.test(module.context) ||
                    /bootstrap\/js/.test(module.context);
            }
        }),
        new webpack.optimize.CommonsChunkPlugin({
            'name'      : 'manifest',
            'minChunks' : Infinity
        }),

        new webpack.ProvidePlugin(oConfig.oProvidePlugin)
    ]
};

Few version:

  • "grunt": "1.0.1"
  • "webpack": "^2.6.1"
  • "webpack-dev-server": "^2.4.5"
  • "grunt-webpack": "^2.0.1"
  • "html-webpack-plugin": "^2.28.0"
  • "html-webpack-plugin": "^2.28.0"
  • "script-ext-html-webpack-plugin": "^1.8.5"
  • "uglifyjs-webpack-plugin": "^0.4.3"
  • "extract-text-webpack-plugin": "^2.1.0"

fix function getScriptName in element.js

webpack config

{
...
plugins:[
  new webpack.optimize.CommonsChunkPlugin({
      name: [`${project.projectName}/js/vendor`, `${project.projectName}/js/manifest`],
      minChunks: Infinity,
      filename: `[name].[chunkhash:8].js`,
    }),
  new ScriptExtHtmlWebpackPlugin({
      inline: 'manifest'
    }),
]

Error: ScriptExtHtmlWebpackPlugin: no asset with href 'manifest.d41d8cd9.js'

modify function getScriptName as bellow, it works well

const getScriptName = (options, tag, compilation) => {
  let scriptName = getRawScriptName(tag);
  // remove publicPath prefix
  // if (scriptName.includes('/')) {
  //   scriptName = scriptName.replace(CONSTANTS.PUBLIC_PATH_PREFIX, '');
  // }
  scriptName = scriptName.replace(compilation.outputOptions.publicPath, '');
  if (options.htmlWebpackOptions.hash) {
    scriptName = scriptName.split('?', 1)[0];
  }
  return scriptName;
};

Provide option for publicPath fallback

When using publicPath (eg CDN), I think it would be useful if there was an option providing a fallback (maybe to local assets) in case of CDN outage. For example, a possible result would look like:

<script src="//cdn.com/chunk1.js"></script> <script>window.xXx || document.write('<script src="/js/chunk1.js"><\/script>')</script>

Support custom value callback

For example:

new ScriptExtHtmlWebpackPlugin({
  custom: [
    {
      test: /\.js$/,
      attribute: 'onload',
      value: (entry) => {return 'alert("loaded ' + entry + '");'}
     }
   ]
}),

Option to add async scripts to the head

Are there any plans to support moving script tags with async to the head, to enable the browser to start fetching the assets as soon as possible?

Thanks for a nice plugin!

Preloading Fonts

Maybe this isn't the right place for this, but I'm just trying to preload my font files, but can't seem to find a way to do it. Is there a way with this plugin to inject the path into the head so they can be preloaded?

webpack 4 not work

when my webpack update to version 4.0.1,it report 'TypeError: callback is not a function'

1.7.2 release broke my config

Confirmed that something between 1.7.0 and 1.7.2 broke. Nothing in the webpack config changed.

Our usage:

plugins: [new ScriptExtHtmlWebpackPlugin({
        defaultAttribute: 'defer'
      })]

Resulting error output:

ERROR in   TypeError: Cannot read property 'options' of undefined

  - plugin.js:35 Compilation.compilation.plugin
    [project-name/[script-ext-html-webpack-plugin]/lib/plugin.js:35:59

  - Tapable.js:206
    [project-name]/[tapable]/lib/Tapable.js:206:14

  - index.js:30 Compilation.<anonymous>
    [project-name]/[html-webpack-exclude-assets-plugin]/index.js:30:7

  - Tapable.js:208 Compilation.applyPluginsAsyncWaterfall
    [project-name]/[tapable]/lib/Tapable.js:208:13

  - util.js:16 Compilation.tryCatcher
    [project-name]/[bluebird]/js/release/util.js:16:23


  - index.js:150
    [project-name]/[html-webpack-plugin]/index.js:150:16

  - util.js:16 tryCatcher
    [project-name]/[bluebird]/js/release/util.js:16:23

  - promise.js:512 Promise._settlePromiseFromHandler
    [project-name]/[bluebird]/js/release/promise.js:512:31

  - promise.js:569 Promise._settlePromise
    [project-name]/[bluebird]/js/release/promise.js:569:18

  - promise.js:614 Promise._settlePromise0
    [project-name]/[bluebird]/js/release/promise.js:614:10

  - promise.js:693 Promise._settlePromises
    [project-name]/[bluebird]/js/release/promise.js:693:18

  - async.js:133 Async._drainQueue
    [project-name]/[bluebird]/js/release/async.js:133:16

  - async.js:143 Async._drainQueues
    [project-name]/[bluebird]/js/release/async.js:143:10

  - async.js:17 Immediate.Async.drainQueues
    [project-name]/[bluebird]/js/release/async.js:17:14

Both defer and async

I tried providing same array file names to async and defer but defer is not getting added as said in README.md regarding defer will be added only when it doesn't meet 1,2,3 conditions. Is it possible to add both these attributes at the same time. It would be really useful!

In short,

	new ScriptExtHTML({
	async: ['manifest', 'vendor', 'app'], // swapping order with below defer also doesn't work
	defer: ['manifest', 'vendor', 'app']
}),

defer attribute isn't added

I don't want my files to be requested for downloading after document is loaded. Yet I want my files to execute in the same order as manifest->vendor->app for which defer and async together is the only choice I've

Thanks πŸ™‚

ScriptExtHtmlWebpackPlugin: no asset with href

below is my webpack config.
`/**

  • @file
  • @author by baidu on 2018/9/12.
    */
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');

module.exports = {
entry: {
index: './src/entry/index.js',
startup: './src/entry/test.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /.sass$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /.san$/,
use: 'san-loader'
},
{
test: /.svg|.jpg|.jpeg|.png$/,
use: 'file-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'san demo',
template: './src/views/layout.tpl',
filename: './views/layout.tpl',
cache: false
}),
new ScriptExtHtmlWebpackPlugin({
inline: ['startup'],
preload: /.js$/,
defaultAttribute: 'async'
})
]
};`

then I got this:

[Question] How to use inline function?

Inline option is not working for me... Can anyone help me?

My web-pack version:
4.16.3
html-webpack-plugin version:
3.2.0

My webpack.config.js:

let path = require('path');
let HTMLWebpackPlugin = require('html-webpack-plugin');
let ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');

module.exports = {
    context: path.resolve(__dirname, 'src'),
    mode: 'development',
    entry: './app.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    plugins: [
        new HTMLWebpackPlugin({
            filename: 'index.html',
        }),
        new ScriptExtHtmlWebpackPlugin({
            inline: 'a.js'
        })
    ]
};

My app.js (entry)
require('./a');

a.js (script I want to inline)
console.log('yes');

Output html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
  <script type="text/javascript" src="bundle.js"></script></body>
</html>

Expect:
a.js is inline to the output html as <script>console.log('yes');</script>

Which is:
Not happening

Preloads are incompatible with webpack-subresource-integrity

The preloads in this plugin are incompatible with webpack-subresource-integrity (SriPlugin).

The SriPlugin adds integrity attributes to chunks and expects you to set WebPack's output.crossOriginLoading to anonymous.

However none of the attributes added to the script are used when creating the preload. As a result Chrome fetches the script twice.

Even if you use a custom attribute to force this plugin to set the crossorigin is should automatically know about, it still loads twice because the integrity also needs to be copied to the preload. Which can't be done manually because the integrity is a generated hash and can't be known ahead of time.

2.0.1 TypeError: Cannot read property 'tap' of undefined

TypeError: Cannot read property 'tap' of undefined
at ScriptExtHtmlWebpackPlugin.compilationCallback (/Users/user/temp/peon-react-redux-test/node_modules/script-ext-html-webpack-plugin/lib/plugin.js:50:56)

while executing the code next line
compilation.hooks.htmlWebpackPluginAlterAssetTags.tap(PLUGIN, alterAssetTags);
'compilation.hooks.htmlWebpackPluginAlterAssetTags' is undefined.
but html-webpack-plugin works well. I'm so confused about it.

script-ext-html-webpack-plugin 2.0.1
html-webpack-plugin 3.2.0
webpack 4.7.0

[Feature] nomodule attribute

I tried to search inside the current issues but I didn't find anything about it.
Currently, the plugin supports type="module" but it doesn't support nomodule attribute.

I know that it is considered experimental but this attribute could give us the leverage to serve a JavaScript file when the module attribute is not supported from the browser.

I image the potential output like this

<script type="module" src="main.js"></script>
<script nomodule src="main.legacy.js"></script>

This feature could help to serve ES-next code inside modern browsers and Legacy code in case module is not supported.

What do you think? Is it doable?

Unable to add custom attributes

Whenever I try to add a custom attribute using this plugin, the attribute just won't get added. I know the 'test' clause is correct because I have added other attributes with that same script matching pattern. Also, no errors appear on the log of the build process.

I am using two ScriptExtHtmlWebpackPlugin declarations since I need not just the 'defer' attribute, but also the custom one on one of the scripts.

Am I missing something? Here is my webpack.config.js:

const path = require("path")
const ExtractTextPlugin = require("extract-text-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CompressionPlugin = require("compression-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin")
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HTMLCompressionPlugin = require("html-compression-webpack-plugin");


const extractSass = new ExtractTextPlugin({
    filename: "[name].[contenthash].css",
    disable: process.env.NODE_ENV === "development"
})

let pathsToClean = [
    'dist'
]

let cleanOptions = {
    exclude: ['server.js', 'package.json', 'node_modules'],
    verbose: true,
    dry: false
}

module.exports = {
    entry: {
        index: "./src/ts/index.ts",
        intercom: "./src/js/intercom.js"
    },
    output: {
        filename: "[name].[hash].js",
        path: path.resolve(__dirname, "dist")
    },
    resolve: {
        extensions: [".webpack.js", ".web.js", ".ts", ".js"],
    },
    module: {
        rules: [{
            test: /\.(scss|css)$/,
            use: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: [{
                    loader: "css-loader",
                    options: {
                        sourceMap: false
                    },
                }, {
                    loader: "sass-loader",
                    options: {
                        sourceMap: false
                    }
                }]
            })
        }, {
            test: /\.tsx?$/,
            use: {
                loader: "ts-loader"
            }
        }, {
            test: /\.(woff2?|woff)$/,
            use: {
                loader: "url-loader"
            },
        }, {
            test: /\.(ttf|eot|svg)$/i,
            use: {
                loader: "file-loader?name=assets/[name].[ext]"
            }
        }, {
            test: /bootstrap-sass\/assets\/javascripts\//,
            use: {
                loader: 'imports-loader?jQuery=jquery'
            },
        }],
    },
    plugins: [
        new ExtractTextPlugin({
            filename: "index.[hash].css"
        }),
        new HtmlWebpackPlugin({
            template: "./src/html/index.html",
            inject: "body",
            title: 'Handy',
            filename: "index.html"
        }),
        new CompressionPlugin({
            asset: "[path].gz[query]",
            algorithm: "gzip",
            test: /\.(js|html|css)$/,
            threshold: 0,
            minRatio: 0.8
        }),
        new HTMLCompressionPlugin({
            testHTML: /\.html$/,
            asset: '[path].gz[query]',
            algorithm: 'gzip',
            threshold: 0,
            minRatio: 0.0,
            deleteOriginalAssets: false
        }),
        new ScriptExtHtmlWebpackPlugin({
            defaultAttribute: 'defer'
        }),
        new ScriptExtHtmlWebpackPlugin({
            custom: [{
                test: 'index',
                attribute: "data-turbolinks-eval",
                value: "false"
            }]
        }),
        new CleanWebpackPlugin(pathsToClean, cleanOptions)

    ],
    watch: true,
    watchOptions: {
        poll: true,
        poll: 500
    }
}

Multiple html plugins

Hi,
I have the following use case. In a webpack config I'm creating several html files as in this question. I can't find any way in the documentation to treat that case with script ext html webpack plugin. That is:

plugins: [
  new HtmlWebpackPlugin({
    filename: 'index.html',
    template: 'src/index.html',
    chunks: ['main']
  }),
  new HtmlWebpackPlugin({
    filename: 'example.html',
    template: 'src/example.html',
    chunks: ['exampleEntry']
  }),
  // I want this to affect only index.html
  new ScriptExtHtmlWebpackPlugin()     
]

Is it possible to do it with this plugin? In the README there are only examples on how to filter the chunks, but not the destination.

Thanks

1.6.0 npm package mistake

It looks like 1.6.0 published to npm misses ./lib directory

I get the following error:

Cannot find module './lib/plugin.js'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
......

Support xhtml

Background

Current behaviour

We currently emit a <script> element with async and defer properties as follows (HTML format):

<script async defer></script>

Required behaviour

The W3 XHTML 1.0 spec gives some recommendations for writing XHTML whilst maintaining compatibility with browsers that only understand HTML:

  1. Attribute minimisation is discouraged

  2. Empty elements are discouraged

  3. Prepending a space before the trailing /> of empty elements is recommended

For the first part, this means that async and defer should have explicit values ("async" and "defer", respectively) rather than being standalone flags with implicit values.

There is some headache-inducing discussion on StackOverflow about the second part, which impacts whether a self-closing element is needed. Summarily: if the web server serves the .xhtml file with text/html MIME type, then W3 recommend to add a closing tag for compatibility with browsers that don't support XHTML. But if targeting browsers that strictly support XHTML, provided the MIME type is application/xhtml+xml, then a self-closing tag will be understood (and furthermore is the recommended formatting).

<!-- If served with MIME: text/html -->
<script async="async" defer="defer"></script>

<!-- If served with MIME: application/xhtml+xml. Note: space before final slash recommended. -->
<script async="async" defer="defer" />

Feature Proposal

So, it would be nice to have a flag for xhtml in the ScriptExtHtmlWebpackPlugin config:

plugins: [
  new HtmlWebpackPlugin({ xhtml: true }),
  new ScriptExtHtmlWebpackPlugin({
    async: 'bundle.js',
    defer: 'bundle.js'
    xhtml: true
  })
]

The default value of the xhtml flag would be false, and possible values would be strict (self-closing tags) or just true (no self-closing tags). I'm open to other possibilities here.

Current support

Note that we can currently achieve all but the self-closing tags currently using the following configuration:

new ScriptExtHtmlWebpackPlugin({
    custom: [
        {
            test: /bundle.js/,
            attribute: 'async',
            value: 'async'
        },
        {
            test: /bundle.js/,
            attribute: 'defer',
            value: 'defer'
        }
    ]
}),

I'm not sure whether the <script> tags are added via appending innerHTML or by using document.appendChild(document.createElement('script')). If the former, then we have control over whether the tags self-close. Otherwise, it's Node's own choice of what to do.

debug module missing with webpack -p option

webpack -p

module.js:327
    throw err;
    ^

Error: Cannot find module 'debug'
    at Function.Module._resolveFilename (module.js:325:15)
    at Function.Module._load (module.js:276:25)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (.../node_modules/script-ext-html-webpack-plugin/index.js:3:15)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (.../webpack.config.js:4:34)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)

[Feature] Support .mjs file extension

Request for .mjs file support.

For the following config, script is not injected into the html

Input:

plugins: [
  new HtmlWebpackPlugin({}),
  new ScriptExtHtmlWebpackPlugin({  module: ['bundle'] }),
],
output: {
  path: `${__dirname}/build`,
  filename: 'bundle.mjs',
}

Current Output:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Webpack App</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
  </head>
  <body></body>
</html>

Required Output:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Webpack App</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
  </head>
  <body>
    <script src="/bundle.js" type="module"></script>
  </body>
</html>

The same config works perfectly fine, if the bundled filename has .js extension.

Plugin could not be registered at 'html-webpack-plugin-alter-asset-tags'. Hook was not found.

script-ext-html-webpack-plugin 1.8.8
with webpack 4.0.1

Error: Plugin could not be registered at 'html-webpack-plugin-alter-asset-tags'. Hook was not found.
BREAKING CHANGE: There need to exist a hook at 'this.hooks'. To create a compatiblity layer for this hook, hook into 'this._pluginCompat'.
at Compilation.plugin (/home/rost/work/pokebase/server/webclient/node_modules/webpack/node_modules/tapable/lib/Tapable.js:63:9)
at Compilation.deprecated [as plugin] (internal/util.js:53:15)
at compiler.plugin (/home/rost/work/pokebase/server/webclient/node_modules/script-ext-html-webpack-plugin/lib/plugin.js:37:19)

TypeError: patterns.some is not a function

webpack.common.js


let plugins = [
new ScriptExtHtmlWebpackPlugin({
            sync: /polyfill|vendor/,
            defaultAttribute: 'async',
            preload: [/polyfill|vendor|main/],
            prefetch: [/chunk/]
        }),
];


ERROR in   TypeError: patterns.some is not a function
  
  - index.js:44 matches
    [ui-util]/[script-ext-html-webpack-plugin]/index.js:44:19
  
  - index.js:60 ATTRIBUTE_PRIORITIES.forEach
    [ui-util]/[script-ext-html-webpack-plugin]/index.js:60:38
  
  - Array.forEach
  
  - index.js:59 generateSrcScriptElement
    [ui-util]/[script-ext-html-webpack-plugin]/index.js:59:24
  
  - index.js:37 htmlPluginData.html.htmlPluginData.html.replace
    [ui-util]/[script-ext-html-webpack-plugin]/index.js:37:14
  
  - String.replace
  
  - index.js:32 replaceScriptElements
    [ui-util]/[script-ext-html-webpack-plugin]/index.js:32:45
  
  - index.js:88 Compilation.compilation.plugin
    [ui-util]/[script-ext-html-webpack-plugin]/index.js:88:11
  
  - Tapable.js:208 Compilation.applyPluginsAsyncWaterfall
    [ui-util]/[tapable]/lib/Tapable.js:208:13
  
  - util.js:16 Compilation.tryCatcher
    [ui-util]/[bluebird]/js/release/util.js:16:23
  
  
  - index.js:159 
    [ui-util]/[html-webpack-plugin]/index.js:159:16
  
  - util.js:16 tryCatcher
    [ui-util]/[bluebird]/js/release/util.js:16:23
  
  - promise.js:512 Promise._settlePromiseFromHandler
    [ui-util]/[bluebird]/js/release/promise.js:512:31
  
  - promise.js:569 Promise._settlePromise
    [ui-util]/[bluebird]/js/release/promise.js:569:18
  
  - promise.js:614 Promise._settlePromise0
    [ui-util]/[bluebird]/js/release/promise.js:614:10
  
  - promise.js:693 Promise._settlePromises
    [ui-util]/[bluebird]/js/release/promise.js:693:18
  
  - async.js:133 Async._drainQueue
    [ui-util]/[bluebird]/js/release/async.js:133:16
  
  - async.js:143 Async._drainQueues
    [ui-util]/[bluebird]/js/release/async.js:143:10
  
  - async.js:17 Immediate.Async.drainQueues
    [ui-util]/[bluebird]/js/release/async.js:17:14
  


Error: ScriptExtHtmlWebpackPlugin: no asset with href

Shouldn't the plugin be looking for something within its src tag?

It happened when I tried to inline a CoffeeScript source with the name animation.coffee, which gets transpiled to animation.js and in development mode, it will get renamed to animation.js?[hash]. Giving the file the async attribute works fine with the following regex: /animation\.*/. What goes wrong here?

plugin does not work with production build

You probably are aware of this issue, but I would like to point it out and ask you if you plan to release fix for this one:

when you build your project with -p flag, webpack removes " from scripts, so this

<script src="some_script.src"></script>

will become this

<script src=some_script.src></script>

and then your regexps won't work.

Furthermore, it would be nice if plugin could just ignore <script> tags without src attributes - there are some cases that you would use this and fill in a dynamical manner, i.e.

<script id="this_will_be_filled"></script>

Please provide me with information if you plan to fix those, so I could use your awesome plugin and not have to build my own! Thanks! :)

`crossorigin` for prefetched chunks

Afaict while there is a way to add the crossorigin attribute to script tags, it is not possible to do the same for link tags. It would be great if this could be added.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.