├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── spec ├── fixtures ├── entry.js └── test.css ├── index.spec.js └── support └── jasmine.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /dist/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - lts/* 5 | - 6.9 6 | script: 7 | - npm test 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jan Nicklas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This plugin is no longer maintained. 2 | 3 | see: https://github.com/icelam/html-inline-script-webpack-plugin 4 | 5 | 6 | Inline Source extension for the HTML Webpack Plugin 7 | ======================================== 8 | [![npm version](https://badge.fury.io/js/html-webpack-inline-source-plugin.svg)](https://badge.fury.io/js/html-webpack-inline-source-plugin) [![Build status](https://travis-ci.org/DustinJackson/html-webpack-inline-source-plugin.svg?branch=master)](https://travis-ci.org/DustinJackson/html-webpack-inline-source-plugin) [![js-semistandard-style](https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg?style=flat-square)](https://github.com/Flet/semistandard) 9 | 10 | Enhances [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) 11 | functionality by adding the `{inlineSource: 'regex string'}` option. 12 | 13 | This is an extension plugin for the [webpack](http://webpack.github.io) plugin [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) (version 4 or higher). It allows you to embed javascript and css source inline. 14 | 15 | Installation 16 | ------------ 17 | You must be running webpack on node 6 or higher. 18 | 19 | Install the plugin with npm: 20 | ```shell 21 | $ npm install --save-dev html-webpack-inline-source-plugin 22 | ``` 23 | 24 | Basic Usage 25 | ----------- 26 | Require the plugin in your webpack config: 27 | 28 | ```javascript 29 | var HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin'); 30 | ``` 31 | 32 | Add the plugin to your webpack config as follows: 33 | 34 | ```javascript 35 | plugins: [ 36 | new HtmlWebpackPlugin(), 37 | new HtmlWebpackInlineSourcePlugin() 38 | ] 39 | ``` 40 | The above configuration will actually do nothing due to the configuration defaults. 41 | 42 | When you set `inlineSource` to a regular expression the source code for any javascript or css file names that match will be embedded inline in the resulting html document. 43 | ```javascript 44 | plugins: [ 45 | new HtmlWebpackPlugin({ 46 | inlineSource: '.(js|css)$' // embed all javascript and css inline 47 | }), 48 | new HtmlWebpackInlineSourcePlugin() 49 | ] 50 | ``` 51 | 52 | Sourcemaps 53 | ---------- 54 | If any source files contain a sourceMappingURL directive that isn't a data URI, then the sourcemap URL is corrected to be relative to the domain root (unless it already is) instead of to the original source file. 55 | 56 | All sourcemap comment styles are supported: 57 | 58 | * `//# ...` 59 | * `//@ ...` 60 | * `/*# ...*/` 61 | * `/*@ ...*/` 62 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'webpack'; 2 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 3 | 4 | export = HtmlWebpackInlineSourcePlugin; 5 | declare class HtmlWebpackInlineSourcePlugin extends Plugin { 6 | constructor(htmlWebpackPlugin: HtmlWebpackPlugin) 7 | } 8 | declare namespace HtmlWebpackInlineSourcePlugin { } 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var escapeRegex = require('escape-string-regexp'); 3 | var path = require('path'); 4 | var slash = require('slash'); 5 | var sourceMapUrl = require('source-map-url'); 6 | 7 | function HtmlWebpackInlineSourcePlugin (htmlWebpackPlugin) { 8 | this.htmlWebpackPlugin = htmlWebpackPlugin; 9 | } 10 | 11 | HtmlWebpackInlineSourcePlugin.prototype.apply = function (compiler) { 12 | var self = this; 13 | 14 | // Hook into the html-webpack-plugin processing 15 | compiler.hooks.compilation.tap('html-webpack-inline-source-plugin', compilation => { 16 | self.htmlWebpackPlugin 17 | .getHooks(compilation) 18 | .alterAssetTagGroups.tapAsync('html-webpack-inline-source-plugin', (htmlPluginData, callback) => { 19 | if (!htmlPluginData.plugin.options.inlineSource) { 20 | return callback(null, htmlPluginData); 21 | } 22 | 23 | var regexStr = htmlPluginData.plugin.options.inlineSource; 24 | 25 | var result = self.processTags(compilation, regexStr, htmlPluginData); 26 | 27 | callback(null, result); 28 | }); 29 | }); 30 | }; 31 | 32 | HtmlWebpackInlineSourcePlugin.prototype.processTags = function (compilation, regexStr, pluginData) { 33 | var self = this; 34 | 35 | var bodyTags = []; 36 | var headTags = []; 37 | 38 | var regex = new RegExp(regexStr); 39 | var filename = pluginData.plugin.options.filename; 40 | 41 | pluginData.headTags.forEach(function (tag) { 42 | headTags.push(self.processTag(compilation, regex, tag, filename)); 43 | }); 44 | 45 | pluginData.bodyTags.forEach(function (tag) { 46 | bodyTags.push(self.processTag(compilation, regex, tag, filename)); 47 | }); 48 | 49 | return { headTags: headTags, bodyTags: bodyTags, plugin: pluginData.plugin, outputName: pluginData.outputName }; 50 | }; 51 | 52 | HtmlWebpackInlineSourcePlugin.prototype.resolveSourceMaps = function (compilation, assetName, asset) { 53 | var source = asset.source(); 54 | var out = compilation.outputOptions; 55 | // Get asset file absolute path 56 | var assetPath = path.join(out.path, assetName); 57 | // Extract original sourcemap URL from source string 58 | if (typeof source !== 'string') { 59 | source = source.toString(); 60 | } 61 | var mapUrlOriginal = sourceMapUrl.getFrom(source); 62 | // Return unmodified source if map is unspecified, URL-encoded, or already relative to site root 63 | if (!mapUrlOriginal || mapUrlOriginal.indexOf('data:') === 0 || mapUrlOriginal.indexOf('/') === 0) { 64 | return source; 65 | } 66 | // Figure out sourcemap file path *relative to the asset file path* 67 | var assetDir = path.dirname(assetPath); 68 | var mapPath = path.join(assetDir, mapUrlOriginal); 69 | var mapPathRelative = path.relative(out.path, mapPath); 70 | // Starting with Node 6, `path` module throws on `undefined` 71 | var publicPath = out.publicPath || ''; 72 | // Prepend Webpack public URL path to source map relative path 73 | // Calling `slash` converts Windows backslashes to forward slashes 74 | var mapUrlCorrected = slash(path.join(publicPath, mapPathRelative)); 75 | // Regex: exact original sourcemap URL, possibly '*/' (for CSS), then EOF, ignoring whitespace 76 | var regex = new RegExp(escapeRegex(mapUrlOriginal) + '(\\s*(?:\\*/)?\\s*$)'); 77 | // Replace sourcemap URL and (if necessary) preserve closing '*/' and whitespace 78 | return source.replace(regex, function (match, group) { 79 | return mapUrlCorrected + group; 80 | }); 81 | }; 82 | 83 | HtmlWebpackInlineSourcePlugin.prototype.processTag = function (compilation, regex, tag, filename) { 84 | var assetUrl; 85 | var preTag = tag; 86 | 87 | // inline js 88 | if (tag.tagName === 'script' && tag.attributes && regex.test(tag.attributes.src)) { 89 | assetUrl = tag.attributes.src; 90 | tag = { 91 | tagName: 'script', 92 | closeTag: true, 93 | attributes: { 94 | type: 'text/javascript' 95 | } 96 | }; 97 | 98 | // inline css 99 | } else if (tag.tagName === 'link' && regex.test(tag.attributes.href)) { 100 | assetUrl = tag.attributes.href; 101 | tag = { 102 | tagName: 'style', 103 | closeTag: true, 104 | attributes: { 105 | type: 'text/css' 106 | } 107 | }; 108 | } 109 | 110 | if (assetUrl) { 111 | // Strip public URL prefix from asset URL to get Webpack asset name 112 | var publicUrlPrefix = compilation.outputOptions.publicPath || ''; 113 | // if filename is in subfolder, assetUrl should be prepended folder path 114 | if (path.basename(filename) !== filename) { 115 | assetUrl = path.dirname(filename) + '/' + assetUrl; 116 | } 117 | var assetName = path.posix.relative(publicUrlPrefix, assetUrl); 118 | var asset = getAssetByName(compilation.assets, assetName); 119 | if (compilation.assets[assetName] !== undefined) { 120 | var updatedSource = this.resolveSourceMaps(compilation, assetName, asset); 121 | tag.innerHTML = (tag.tagName === 'script') ? updatedSource.replace(/(<)(\/script>)/g, '\\x3C$2') : updatedSource; 122 | }else{ 123 | return preTag; 124 | } 125 | } 126 | 127 | return tag; 128 | }; 129 | 130 | function getAssetByName (assests, assetName) { 131 | for (var key in assests) { 132 | if (assests.hasOwnProperty(key)) { 133 | var processedKey = path.posix.relative('', key); 134 | if (processedKey === assetName) { 135 | return assests[key]; 136 | } 137 | } 138 | } 139 | } 140 | 141 | module.exports = HtmlWebpackInlineSourcePlugin; 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-webpack-inline-source-plugin", 3 | "version": "1.0.0-beta.2", 4 | "description": "Embed javascript and css source inline when using the webpack dev server or middleware", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "files": [ 8 | "index.js", 9 | "index.d.ts" 10 | ], 11 | "scripts": { 12 | "prepublish": "npm run test", 13 | "pretest": "semistandard", 14 | "test": "jasmine", 15 | "debug": "node-debug jasmine" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/dustinjackson/html-webpack-inline-source-plugin.git" 20 | }, 21 | "keywords": [ 22 | "webpack", 23 | "plugin", 24 | "html-webpack-plugin", 25 | "inline", 26 | "source" 27 | ], 28 | "author": "Dustin Jackson (https://github.com/DustinJackson)", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/dustinjackson/html-webpack-inline-source-plugin/issues" 32 | }, 33 | "homepage": "https://github.com/dustinjackson/html-webpack-inline-source-plugin", 34 | "devDependencies": { 35 | "cheerio": "^0.22.0", 36 | "css-loader": "^0.25.0", 37 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 38 | "html-webpack-plugin": "^4.0.0-beta.4", 39 | "jasmine": "^2.4.1", 40 | "rimraf": "^2.5.2", 41 | "semistandard": "^7.0.5", 42 | "style-loader": "^0.13.1", 43 | "webpack": "^4.20.2" 44 | }, 45 | "dependencies": { 46 | "escape-string-regexp": "^1.0.5", 47 | "slash": "^1.0.0", 48 | "source-map-url": "^0.4.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spec/fixtures/entry.js: -------------------------------------------------------------------------------- 1 | require('./test.css'); 2 | console.log('.embedded.source'); 3 | -------------------------------------------------------------------------------- /spec/fixtures/test.css: -------------------------------------------------------------------------------- 1 | .embedded.source { } -------------------------------------------------------------------------------- /spec/index.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jasmine */ 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var cheerio = require('cheerio'); 5 | var webpack = require('webpack'); 6 | var rm_rf = require('rimraf'); 7 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 8 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 9 | var HtmlWebpackInlineSourcePlugin = require('../'); 10 | 11 | var OUTPUT_DIR = path.join(__dirname, '../dist'); 12 | 13 | describe('HtmlWebpackInlineSourcePlugin', function () { 14 | beforeEach(function (done) { 15 | rm_rf(OUTPUT_DIR, done); 16 | }); 17 | 18 | it('should not inline source by default', function (done) { 19 | webpack({ 20 | entry: path.join(__dirname, 'fixtures', 'entry.js'), 21 | output: { 22 | path: OUTPUT_DIR 23 | }, 24 | module: { 25 | rules: [{ test: /\.css$/, use: ExtractTextPlugin.extract({ 26 | fallback: 'style-loader', 27 | use: 'css-loader' 28 | }) }] 29 | }, 30 | plugins: [ 31 | new ExtractTextPlugin('style.css'), 32 | new HtmlWebpackPlugin(), 33 | new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin) 34 | ] 35 | }, function (err) { 36 | expect(err).toBeFalsy(); 37 | var htmlFile = path.resolve(OUTPUT_DIR, 'index.html'); 38 | fs.readFile(htmlFile, 'utf8', function (er, data) { 39 | expect(er).toBeFalsy(); 40 | var $ = cheerio.load(data); 41 | expect($('script[src="bundle.js"]').html()).toBeNull(); 42 | expect($('link[href="style.css"]').html()).toBe(''); 43 | done(); 44 | }); 45 | }); 46 | }); 47 | 48 | it('should embed sources inline when regex matches file names', function (done) { 49 | webpack({ 50 | entry: path.join(__dirname, 'fixtures', 'entry.js'), 51 | output: { 52 | // filename with directory tests sourcemap URL correction 53 | filename: 'bin/app.js', 54 | // public path required to test sourcemap URL correction, but also for this bug work-around: 55 | // https://github.com/webpack/webpack/issues/3242#issuecomment-260411104 56 | publicPath: '/assets', 57 | path: OUTPUT_DIR 58 | }, 59 | module: { 60 | rules: [{ test: /\.css$/, use: ExtractTextPlugin.extract({ 61 | fallback: 'style-loader', 62 | use: 'css-loader' 63 | }) }] 64 | }, 65 | // generate sourcemaps for testing URL correction 66 | devtool: '#source-map', 67 | plugins: [ 68 | new ExtractTextPlugin('style.css'), 69 | new HtmlWebpackPlugin({ 70 | inlineSource: '.(js|css)$' 71 | }), 72 | new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin) 73 | ] 74 | }, function (err) { 75 | expect(err).toBeFalsy(); 76 | var htmlFile = path.resolve(OUTPUT_DIR, 'index.html'); 77 | fs.readFile(htmlFile, 'utf8', function (er, data) { 78 | expect(er).toBeFalsy(); 79 | var $ = cheerio.load(data); 80 | expect($('script').html()).toContain('.embedded.source'); 81 | expect($('script').html()).toContain('//# sourceMappingURL=/assets/bin/app.js.map'); 82 | expect($('style').html()).toContain('.embedded.source'); 83 | expect($('style').html()).toContain('/*# sourceMappingURL=/assets/style.css.map'); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | 89 | it('should embed sources inline even if a query string hash is used', function (done) { 90 | webpack({ 91 | entry: path.join(__dirname, 'fixtures', 'entry.js'), 92 | output: { 93 | // filename with output hash 94 | filename: 'app.js?[hash]', 95 | path: OUTPUT_DIR 96 | }, 97 | module: { 98 | rules: [{ test: /\.css$/, use: ExtractTextPlugin.extract({ 99 | fallback: 'style-loader', 100 | use: 'css-loader' 101 | }) }] 102 | }, 103 | plugins: [ 104 | new ExtractTextPlugin('style.css?[hash]'), 105 | new HtmlWebpackPlugin({ 106 | // modified regex to accept query string 107 | inlineSource: '.(js|css)(\\?.*)?$' 108 | }), 109 | new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin) 110 | ] 111 | }, function (err) { 112 | expect(err).toBeFalsy(); 113 | var htmlFile = path.resolve(OUTPUT_DIR, 'index.html'); 114 | fs.readFile(htmlFile, 'utf8', function (er, data) { 115 | expect(er).toBeFalsy(); 116 | var $ = cheerio.load(data); 117 | expect($('script').html()).toContain('.embedded.source'); 118 | expect($('style').html()).toContain('.embedded.source'); 119 | done(); 120 | }); 121 | }); 122 | }); 123 | 124 | it('should embed source and not error if public path is undefined', function (done) { 125 | webpack({ 126 | entry: path.join(__dirname, 'fixtures', 'entry.js'), 127 | output: { 128 | filename: 'bin/app.js', 129 | path: OUTPUT_DIR 130 | }, 131 | module: { 132 | rules: [{ test: /\.css$/, use: ExtractTextPlugin.extract({ 133 | fallback: 'style-loader', 134 | use: 'css-loader' 135 | }) }] 136 | }, 137 | plugins: [ 138 | new ExtractTextPlugin('style.css'), 139 | new HtmlWebpackPlugin({ 140 | inlineSource: '.(js|css)$' 141 | }), 142 | new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin) 143 | ] 144 | }, function (err) { 145 | expect(err).toBeFalsy(); 146 | var htmlFile = path.resolve(OUTPUT_DIR, 'index.html'); 147 | fs.readFile(htmlFile, 'utf8', function (er, data) { 148 | expect(er).toBeFalsy(); 149 | var $ = cheerio.load(data); 150 | expect($('script').html()).toContain('.embedded.source'); 151 | expect($('style').html()).toContain('.embedded.source'); 152 | done(); 153 | }); 154 | }); 155 | }); 156 | 157 | it('should embed source and not error if html in subfolder', function (done) { 158 | webpack({ 159 | entry: path.join(__dirname, 'fixtures', 'entry.js'), 160 | output: { 161 | filename: 'bin/app.js', 162 | path: OUTPUT_DIR 163 | }, 164 | module: { 165 | rules: [{ test: /\.css$/, use: ExtractTextPlugin.extract({ 166 | fallback: 'style-loader', 167 | use: 'css-loader' 168 | }) }] 169 | }, 170 | plugins: [ 171 | new ExtractTextPlugin('style.css'), 172 | new HtmlWebpackPlugin({ 173 | filename: 'subfolder/index.html', 174 | inlineSource: '.(js|css)$' 175 | }), 176 | new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin) 177 | ] 178 | }, function (err) { 179 | expect(err).toBeFalsy(); 180 | var htmlFile = path.resolve(OUTPUT_DIR, 'subfolder/index.html'); 181 | fs.readFile(htmlFile, 'utf8', function (er, data) { 182 | expect(er).toBeFalsy(); 183 | var $ = cheerio.load(data); 184 | expect($('script').html()).toContain('.embedded.source'); 185 | expect($('style').html()).toContain('.embedded.source'); 186 | done(); 187 | }); 188 | }); 189 | }); 190 | }); 191 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "stopSpecOnExpectationFailure": false, 7 | "random": true 8 | } 9 | --------------------------------------------------------------------------------