This repository has been archived by the owner on Apr 5, 2021. It is now read-only.
v3.3.5
What's New in v3.3.5
This is a summary of the differences between v3.3.5 and v3.3.4.
Commits
Show commits
SHA | Author | Message |
---|---|---|
438ae30 |
mmiller42 | CircleCI setup |
67a24b4 |
mmiller42 | CircleCI setup |
6b159ec |
mmiller42 | Naming steps |
73386cd |
mmiller42 | Trying to fix semver bug? |
c934e78 |
mmiller42 | Trying to fix semver bug? |
02dd50a |
mmiller42 | sudoit |
35af31d |
mmiller42 | build step |
0d68b81 |
mmiller42 | testing tests! |
bc01e19 |
mmiller42 | Unit test coverage! And some bugs I fixed along the way. |
1906e18 |
mmiller42 | Add publish configuration and CI badge |
2321037 |
mmiller42 | 3.3.5 |
07fa240 |
mmiller42 | Publish based on commit message instead of tag, since tags aren't supported in CircleCI 2.0 |
6a59200 |
mmiller42 | reversing version manually |
a787f60 |
mmiller42 | 3.3.5 |
Changed files
➕ .circleci/config.yml
Show changes
@@ -0,0 +1,41 @@
+version: 2
+jobs:
+ build:
+ docker:
+ - image: circleci/node:latest
+ working_directory: ~/html-webpack-externals-plugin
+
+ steps:
+ - checkout
+ - run:
+ name: Authenticate to npm registry
+ command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
+ - run:
+ name: Update npm
+ command: |
+ npm install npm@latest --no-save &&
+ sudo rm -rf /usr/local/lib/node_modules/npm &&
+ sudo mv node_modules/npm /usr/local/lib/node_modules/npm
+ - restore_cache:
+ key: dependency-cache-{{ checksum "package.json" }}
+ - run:
+ name: Install dependencies
+ command: npm install
+ - save_cache:
+ key: dependency-cache-{{ checksum "package.json" }}
+ paths:
+ - node_modules
+ - run:
+ name: Build
+ command: npm run build
+ - run:
+ name: Test
+ command: npm test
+ - deploy:
+ name: Publish
+ command: |
+ if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$"; then
+ npm publish
+ else
+ echo "No tag pushed, skipping deploy"
+ fi
.gitignore
Show changes
@@ -6,11 +6,14 @@
# Dependencies
node_modules
-
+˚
# Logs
*.log
.node_repl_history
.node_history
# Build files
lib
+
+# Temporary files
+tmp
.npmignore
Show changes
@@ -11,3 +11,6 @@ node_modules
*.log
.node_repl_history
.node_history
+
+# Temporary files
+tmp
README.md
Show changes
@@ -1,4 +1,4 @@
-# html-webpack-externals-plugin
+# html-webpack-externals-plugin [![CircleCI](https://circleci.com/gh/mmiller42/html-webpack-externals-plugin.svg?style=svg)](https://circleci.com/gh/mmiller42/html-webpack-externals-plugin)
Webpack plugin that works alongside [\`html-webpack-plugin\`](https://github.com/jantimon/html-webpack-plugin) to use pre-packaged vendor bundles.
@@ -251,7 +251,7 @@ You should include a trailing slash in your public path, and a leading slash if
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/dist/css/bootstrap.min.css\`
-1. adds \`<link href="/public/vendor/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">\` to your HTML file, before your chunks
+1. adds \`<link href="/assets/vendor/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">\` to your HTML file, before your chunks
\`\`\`js
new HtmlWebpackExternalsPlugin({
package-lock.json
Inline diff not displayed. View the whole file
package.json
Show changes
@@ -1,6 +1,6 @@
{
"name": "html-webpack-externals-plugin",
- "version": "3.3.4",
+ "version": "3.3.5",
"description": "Webpack plugin that works alongside html-webpack-plugin to use pre-packaged vendor bundles.",
"keywords": [
"htmlwebpackplugin",
@@ -12,12 +12,13 @@
"scripts": {
"prepack": "npm run build",
"precommit": "lint-staged",
+ "test": "mocha --require babel-register",
"build": "rm -rf lib && babel src --out-dir lib --source-maps --copy-files",
"watch": "npm run build -- --watch"
},
"lint-staged": {
"*.js": [
- "prettier --write --no-semi --single-quote --trailing-comma es5 '{src/**/*.{js,json},*.json}'",
+ "prettier --write --no-semi --single-quote --trailing-comma es5 '{src/**/*.{js,json},test/**/*.js,*.json}'",
"git add"
]
},
@@ -41,9 +42,19 @@
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-preset-env": "^1.6.0",
+ "babel-register": "^6.24.1",
+ "bootstrap": "^3.3.7",
+ "css-loader": "^0.28.4",
+ "escape-string-regexp": "^1.0.5",
+ "extract-text-webpack-plugin": "^3.0.0",
+ "html-webpack-plugin": "^2.0.0",
"husky": "^0.14.3",
- "lint-staged": "^4.0.1",
- "prettier": "^1.5.3"
+ "jquery": "^3.2.1",
+ "lint-staged": "^4.0.2",
+ "mocha": "^3.4.2",
+ "prettier": "^1.5.3",
+ "rimraf": "^2.6.1",
+ "webpack": "^3.3.0"
},
"peerDependencies": {
"html-webpack-plugin": "^2.0.0"
src/HtmlWebpackExternalsPlugin.js
Show changes
@@ -74,10 +74,15 @@ export default class HtmlWebpackExternalsPlugin {
}
}
- const publicPath =
- this.publicPath == null
- ? compiler.options.output.publicPath
- : this.publicPath
+ const publicPath = (() => {
+ if (this.publicPath != null) {
+ return this.publicPath
+ } else if (compiler.options.output.publicPath != null) {
+ return compiler.options.output.publicPath
+ } else {
+ return ''
+ }
+ })()
const pluginsToApply = []
➕ test/HtmlWebpackExternalsPlugin.spec.js
Show changes
@@ -0,0 +1,298 @@
+import assert from 'assert'
+import HtmlWebpackPlugin from 'html-webpack-plugin'
+import HtmlWebpackExternalsPlugin from '../lib/'
+import {
+ cleanUp,
+ runWebpack,
+ checkBundleExcludes,
+ checkCopied,
+ checkHtmlIncludes,
+} from './utils'
+
+describe('HtmlWebpackExternalsPlugin', function() {
+ afterEach(cleanUp)
+
+ it('validates the arguments passed to the constructor', function() {
+ assert.throws(
+ () => new HtmlWebpackExternalsPlugin({}),
+ /should have required property 'externals'/
+ )
+ })
+
+ it('Local JS external example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'jquery',
+ entry: 'dist/jquery.min.js',
+ global: 'jQuery',
+ },
+ ],
+ })
+ )
+ .then(() => checkBundleExcludes('jQuery'))
+ .then(() => checkCopied('vendor/jquery/dist/jquery.min.js'))
+ .then(() => checkHtmlIncludes('vendor/jquery/dist/jquery.min.js', 'js'))
+ })
+
+ it('Local CSS external example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ },
+ ],
+ })
+ )
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkHtmlIncludes('vendor/bootstrap/dist/css/bootstrap.min.css', 'css')
+ )
+ })
+
+ it('Local external with supplemental assets example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ supplements: ['dist/fonts/'],
+ },
+ ],
+ })
+ )
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkCopied(
+ 'vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.eot'
+ )
+ )
+ .then(() =>
+ checkHtmlIncludes('vendor/bootstrap/dist/css/bootstrap.min.css', 'css')
+ )
+ })
+
+ it('CDN example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'jquery',
+ entry: 'https://unpkg.com/[email protected]/dist/jquery.min.js',
+ global: 'jQuery',
+ },
+ ],
+ })
+ )
+ .then(() => checkBundleExcludes('jQuery'))
+ .then(() =>
+ checkHtmlIncludes(
+ 'https://unpkg.com/[email protected]/dist/jquery.min.js',
+ 'js'
+ )
+ )
+ })
+
+ it('URL without implicit extension example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'google-roboto',
+ entry: {
+ path: 'https://fonts.googleapis.com/css?family=Roboto',
+ type: 'css',
+ },
+ },
+ ],
+ })
+ ).then(() =>
+ checkHtmlIncludes('https://fonts.googleapis.com/css?family=Roboto', 'css')
+ )
+ })
+
+ it('Module with multiple entry points example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: [
+ 'dist/css/bootstrap.min.css',
+ 'dist/css/bootstrap-theme.min.css',
+ ],
+ supplements: ['dist/fonts/'],
+ },
+ ],
+ })
+ )
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkCopied('vendor/bootstrap/dist/css/bootstrap-theme.min.css')
+ )
+ .then(() =>
+ checkCopied(
+ 'vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.eot'
+ )
+ )
+ .then(() =>
+ checkHtmlIncludes('vendor/bootstrap/dist/css/bootstrap.min.css', 'css')
+ )
+ .then(() =>
+ checkHtmlIncludes(
+ 'vendor/bootstrap/dist/css/bootstrap-theme.min.css',
+ 'css'
+ )
+ )
+ })
+
+ it('Appended assets example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ append: true,
+ },
+ ],
+ })
+ )
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkHtmlIncludes(
+ 'vendor/bootstrap/dist/css/bootstrap.min.css',
+ 'css',
+ true
+ )
+ )
+ })
+
+ it('Cache-busting with hashes example', function() {
+ let hash
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ },
+ ],
+ hash: true,
+ })
+ )
+ .then(stats => {
+ hash = stats.toJson().hash
+ })
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkHtmlIncludes(
+ \`vendor/bootstrap/dist/css/bootstrap.min.css?${hash}\`,
+ 'css'
+ )
+ )
+ })
+
+ it('Customizing output path example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ },
+ ],
+ outputPath: 'thirdparty',
+ })
+ )
+ .then(() =>
+ checkCopied('thirdparty/bootstrap/dist/css/bootstrap.min.css')
+ )
+ .then(() =>
+ checkHtmlIncludes(
+ 'thirdparty/bootstrap/dist/css/bootstrap.min.css',
+ 'css'
+ )
+ )
+ })
+
+ it('Customizing public path example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin(),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ },
+ ],
+ publicPath: '/assets/',
+ })
+ )
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkHtmlIncludes(
+ '/assets/vendor/bootstrap/dist/css/bootstrap.min.css',
+ 'css'
+ )
+ )
+ })
+
+ it('Specifying which HTML files to affect example', function() {
+ return runWebpack(
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ }),
+ new HtmlWebpackPlugin({
+ filename: 'about.html',
+ }),
+ new HtmlWebpackExternalsPlugin({
+ externals: [
+ {
+ module: 'bootstrap',
+ entry: 'dist/css/bootstrap.min.css',
+ },
+ ],
+ files: ['about.html'],
+ })
+ )
+ .then(() => checkCopied('vendor/bootstrap/dist/css/bootstrap.min.css'))
+ .then(() =>
+ checkHtmlIncludes(
+ 'vendor/bootstrap/dist/css/bootstrap.min.css',
+ 'css',
+ false,
+ 'about.html'
+ )
+ )
+ .then(() => {
+ return new Promise((resolve, reject) => {
+ checkHtmlIncludes(
+ 'vendor/bootstrap/dist/css/bootstrap.min.css',
+ 'css',
+ false,
+ 'index.html'
+ )
+ .then(() =>
+ reject(
+ 'index.html should not have had the assets inserted into the HTML'
+ )
+ )
+ .catch(() => resolve())
+ })
+ })
+ })
+})
➕ test/fixtures/app.js
Show changes
@@ -0,0 +1,3 @@
+const $ = require('jquery')
+
+$('body').css('background', 'red')
➕ test/fixtures/style.css
Show changes
@@ -0,0 +1,3 @@
+body {
+ color: blue;
+}
➕ test/utils/index.js
Show changes
@@ -0,0 +1,126 @@
+import path from 'path'
+import fs from 'fs'
+import assert from 'assert'
+import webpack from 'webpack'
+import ExtractTextPlugin from 'extract-text-webpack-plugin'
+import escapeRegExp from 'escape-string-regexp'
+import rimraf from 'rimraf'
+
+const OUTPUT_PATH = path.resolve(__dirname, '..', 'tmp')
+
+export function cleanUp() {
+ return promisify(rimraf, OUTPUT_PATH)
+}
+
+export function runWebpack(...plugins) {
+ return promisify(webpack, {
+ entry: {
+ app: path.resolve(__dirname, '..', 'fixtures', 'app.js'),
+ style: path.resolve(__dirname, '..', 'fixtures', 'style.css'),
+ },
+ output: {
+ path: OUTPUT_PATH,
+ filename: '[name].js',
+ },
+ module: {
+ loaders: [
+ {
+ test: /\.css$/,
+ loader: ExtractTextPlugin.extract({ use: 'css-loader' }),
+ },
+ ],
+ },
+ plugins: [new ExtractTextPlugin({ filename: '[name].css' }), ...plugins],
+ }).then(stats => {
+ assert.strictEqual(
+ stats.hasErrors(),
+ false,
+ stats.toJson().errors.toString()
+ )
+ return stats
+ })
+}
+
+export function checkBundleExcludes(external) {
+ return promisify(
+ fs.readFile,
+ path.join(OUTPUT_PATH, 'app.js'),
+ 'utf8'
+ ).then(contents => {
+ assert.ok(
+ contents.indexOf(\`module.exports = ${external}\`) > -1,
+ \`${external} was not excluded from the bundle\`
+ )
+ })
+}
+
+export function checkCopied(file) {
+ return promisify(fs.access, path.join(OUTPUT_PATH, file))
+}
+
+export function checkHtmlIncludes(
+ file,
+ type,
+ append = false,
+ htmlFile = 'index.html'
+) {
+ return promisify(
+ fs.readFile,
+ path.join(OUTPUT_PATH, htmlFile),
+ 'utf8'
+ ).then(contents => {
+ if (type === 'js') {
+ assert.ok(
+ contents.match(new RegExp(\`<script.*src="${escapeRegExp(file)}".*>\`)),
+ \`${file} script was not inserted into the HTML output\`
+ )
+ } else if (type === 'css') {
+ assert.ok(
+ contents.match(new RegExp(\`<link.*href="${escapeRegExp(file)}".*>\`)),
+ \`${file} link was not inserted into the HTML output\`
+ )
+ }
+
+ assert.ok(
+ inequal(
+ append ? '<' : '>',
+ contents.indexOf(type === 'js' ? 'app.js' : 'style.css'),
+ contents.indexOf(file)
+ ),
+ \`${file} should have been inserted ${append
+ ? 'after'
+ : 'before'} the bundle\`
+ )
+ })
+}
+
+export function promisify(fn, ...args) {
+ return new Promise((resolve, reject) => {
+ fn(...args, (err, result) => {
+ if (err) {
+ reject(err)
+ return
+ }
+ resolve(result)
+ })
+ })
+}
+
+function inequal(operator, a, b) {
+ switch (operator) {
+ case '<':
+ return a < b
+ case '>':
+ return a > b
+ case '<=':
+ return a <= b
+ case '>=':
+ return a >= b
+ case '!=':
+ return a != b
+ case '!==':
+ return a !== b
+ default:
+ throw new Error(\`Unknown operator ${operator}\`)
+ }
+}