Skip to content

Commit

Permalink
feat: Support webpack 4 splitChunk option
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Drop support for Webpack version < 4
  • Loading branch information
melck committed Dec 24, 2019
1 parent 4a29433 commit d1f8e39
Show file tree
Hide file tree
Showing 16 changed files with 2,709 additions and 899 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ insert_final_newline = true
[*.json]
insert_final_newline = ignore

# Makefiles always use tabs for indentation
[*.sh]
indent_size = 4

# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab
Expand Down
21 changes: 15 additions & 6 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended",
],
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended"],
"rules": {
"@typescript-eslint/indent": "off",
"@typescript-eslint/no-var-requires": "off"
}
"@typescript-eslint/no-var-requires": "off",
// disable the rule for all files
"@typescript-eslint/explicit-function-return-type": "off"
},
"overrides": [
{
// enable the rule specifically for TypeScript files
"files": ["*.ts", "*.tsx"],
"rules": {
"@typescript-eslint/explicit-function-return-type": ["error"]
}
}
]
}
30 changes: 0 additions & 30 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,6 @@ stages:

jobs:
include:
- name: Tests Webpack 1
stage: test
language: node_js
node_js:
- "12"
- "11"
before_install:
- yarn global add webpack@^1.0.0
- export NODE_PATH=$(yarn global dir)/node_modules
script: yarn run travis
- name: Tests Webpack 2
stage: test
language: node_js
node_js:
- "12"
- "11"
before_install:
- yarn global add webpack@^2.0.0
- export NODE_PATH=$(yarn global dir)/node_modules
script: yarn run travis
- name: Tests Webpack 3
stage: test
language: node_js
node_js:
- "12"
- "11"
before_install:
- yarn global add webpack@^3.0.0
- export NODE_PATH=$(yarn global dir)/node_modules
script: yarn run travis
- name: Tests Webpack 4
stage: test
language: node_js
Expand Down
33 changes: 0 additions & 33 deletions lib/index.d.ts

This file was deleted.

246 changes: 140 additions & 106 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,133 +1,167 @@
var path = require('path');
var fs = require('fs');
var stripAnsi = require('strip-ansi');
var mkdirp = require('mkdirp');
var extend = require('deep-extend');

// @ts-ignore
var assets = {};
var DEFAULT_OUTPUT_FILENAME = 'webpack-stats.json';
var DEFAULT_LOG_TIME = false;

function Plugin(options) {
// @ts-ignore
this.contents = {};
// @ts-ignore
this.options = options || {};
// @ts-ignore
this.options.filename = this.options.filename || DEFAULT_OUTPUT_FILENAME;
// @ts-ignore
if (this.options.logTime === undefined) {
// @ts-ignore
this.options.logTime = DEFAULT_LOG_TIME;
}
}
// @ts-check
/** @typedef {import("lodash.defaults")} defaults */
/** @typedef {import("lodash.assign")} assign */
/** @typedef {import("lodash.get")} get */
/** @typedef {import("webpack").Compiler} Compiler */
/** @typedef {import("webpack").Stats} Stats */
/** @typedef {import("webpack").compilation.Compilation} Compilation */
/** @typedef {import("webpack").compilation.ContextModuleFactory} ContextModuleFactory */
/** @typedef {import("webpack").ChunkData} ChunkData */
/** @typedef {import("../typings").Contents} Contents */
/** @typedef {import("../typings").Options} Options */

Plugin.prototype.apply = function(compiler) {
var self = this;
const ouputDirChunk = path.resolve(compiler.options.output.path || process.cwd());
const ouputDirTracker = path.resolve(self.options.path || compiler.options.output.path || process.cwd());
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');

// @ts-ignore
const _compilation = function(compilation, callback) {
const failedModule = function(fail) {
var output = {
status: 'error',
error: fail.error.name || 'unknown-error',
};
if (fail.error.module !== undefined) {
output.file = fail.error.module.userRequest;
}
if (fail.error.error !== undefined) {
// @ts-ignore
output.message = stripAnsi(fail.error.error.codeFrame);
} else {
output.message = '';
}
self.writeOutput(compiler, output);
const defaults = require('lodash.defaults');
const assign = require('lodash.assign');
const get = require('lodash.get');
const stripAnsi = require('strip-ansi');

class BundleTrackerPlugin {
/**
* Track assets file location per bundle
* @param {Options} options
*/
constructor(options) {
/** @type {Options} */
this.options = options;
/** @type {Contents} */
this.contents = {
status: 'initialization',
chunks: {},
};
this.name = 'BundleTrackerPlugin';

this.outputChunkDir = '';
this.outputTrackerFile = '';
this.ouputTrackerDir = '';
}
/**
* Setup parameter from compiler data
* @param {Compiler} compiler
* @returns this
*/
_setParamsFromCompiler(compiler) {
this.options = defaults({}, this.options, {
path: get(compiler.options, 'output.path', process.cwd()),
publicPath: get(compiler.options, 'output.publicPath', ''),
filename: 'webpack-stats.json',
logTime: false,
relativePath: false,
integrity: false,
// https://www.w3.org/TR/SRI/#cryptographic-hash-functions
integrityHashes: ['sha256', 'sha384', 'sha512'],
});

// Set output directories
this.outputChunkDir = path.resolve(get(compiler.options, 'output.path', process.cwd()));
this.outputTrackerFile = path.resolve(path.join(this.options.path, this.options.filename));
this.ouputTrackerDir = path.dirname(this.outputTrackerFile);

return this;
}
/**
* Write bundle tracker stats file
*
* @param {Compiler} compiler
* @param {Contents} contents
*/
_writeOutput(compiler, contents) {
assign(this.contents, contents);

if (compilation.hooks) {
const plugin = { name: 'BundleTrackerPlugin' };
compilation.hooks.failedModule.tap(plugin, failedModule);
} else {
compilation.plugin('failed-module', failedModule);
if (this.options.publicPath) {
this.contents.publicPath = this.options.publicPath;
}
};

// @ts-ignore
const compile = function(factory, callback) {
self.writeOutput(compiler, { status: 'compiling' });
};
fs.mkdirSync(this.ouputTrackerDir, { recursive: true, mode: 0o755 });
fs.writeFileSync(this.outputTrackerFile, JSON.stringify(this.contents, null, this.options.indent));
}
/**
* Compute hash for a content
* @param {string} content
*/
_computeIntegrity(content) {
return this.options.integrityHashes
.map(algorithm => {
const hash = crypto
.createHash(algorithm)
.update(content, 'utf8')
.digest('base64');

const done = function(stats) {
if (stats.compilation.errors.length > 0) {
var error = stats.compilation.errors[0];
self.writeOutput(compiler, {
return `${algorithm}-${hash}`;
})
.join(' ');
}
/**
* Handle compile hook
* @param {Compiler} compiler
*/
_handleCompile(compiler) {
this._writeOutput(compiler, { status: 'compile' });
}
/**
* Handle compile hook
* @param {Compiler} compiler
* @param {Stats} stats
*/
_handleDone(compiler, stats) {
if (stats.hasErrors()) {
const error = stats.compilation.errors[0];
this._writeOutput(compiler, {
status: 'error',
error: error['name'] || 'unknown-error',
// @ts-ignore
error: get(error, 'name', 'unknown-error'),
message: stripAnsi(error['message']),
});

return;
}

var chunks = {};
stats.compilation.chunks.map(function(chunk) {
var files = chunk.files.map(function(file) {
var F = { name: file };
var publicPath = self.options.publicPath || compiler.options.output.publicPath;
if (publicPath) {
F.publicPath = publicPath + file;
const output = { status: 'done', chunks: {} };
stats.compilation.chunkGroups.map(chunkGroup => {
if (!chunkGroup.isInitial()) return;

output.chunks[chunkGroup.name] = chunkGroup.getFiles().map(filename => {
const fileInfo = { name: filename };
const file = stats.compilation.assets[filename];

if (this.options.integrity === true) {
fileInfo.integrity = this._computeIntegrity(file.source());
}

if (this.options.publicPath) {
fileInfo.publicPath = this.options.publicPath + filename;
}
if (self.options.relativePath === true) {
const absoluteFilePath = path.join(ouputDirChunk, file);
F.path = path.relative(ouputDirTracker, absoluteFilePath);

if (this.options.relativePath === true) {
const absoluteFilePath = path.join(this.outputChunkDir, filename);
fileInfo.path = path.relative(this.ouputTrackerDir, absoluteFilePath);
} else {
F.path = path.join(ouputDirChunk, file);
fileInfo.path = path.join(this.outputChunkDir, filename);
}

return F;
return fileInfo;
});
chunks[chunk.name] = files;
});
var output = {
status: 'done',
chunks: chunks,
};

if (self.options.logTime === true) {
if (this.options.logTime === true) {
output.startTime = stats.startTime;
output.endTime = stats.endTime;
}

self.writeOutput(compiler, output);
};

if (compiler.hooks) {
const plugin = { name: 'BundleTrackerPlugin' };
compiler.hooks.compilation.tap(plugin, _compilation);
compiler.hooks.compile.tap(plugin, compile);
compiler.hooks.done.tap(plugin, done);
} else {
compiler.plugin('compilation', _compilation);
compiler.plugin('compile', compile);
compiler.plugin('done', done);
}
};

Plugin.prototype.writeOutput = function(compiler, contents) {
var outputDir = this.options.path || '.';
var outputFilename = path.join(outputDir, this.options.filename || DEFAULT_OUTPUT_FILENAME);
var publicPath = this.options.publicPath || compiler.options.output.publicPath;
if (publicPath) {
contents.publicPath = publicPath;
this._writeOutput(compiler, output);
}
mkdirp.sync(path.dirname(outputFilename));
/**
* Method called by webpack to apply plugin hook
* @param {Compiler} compiler
*/
apply(compiler) {
this._setParamsFromCompiler(compiler);

this.contents = extend(this.contents, contents);
// @ts-ignore
fs.writeFileSync(outputFilename, JSON.stringify(this.contents, null, this.options.indent));
};
compiler.hooks.compile.tap(this.name, this._handleCompile.bind(this, compiler));
compiler.hooks.done.tap(this.name, this._handleDone.bind(this, compiler));
}
}

module.exports = Plugin;
module.exports = BundleTrackerPlugin;
Loading

0 comments on commit d1f8e39

Please sign in to comment.