-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support webpack 4 splitChunk option
BREAKING CHANGE: Drop support for Webpack version < 4
- Loading branch information
Showing
16 changed files
with
2,709 additions
and
899 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.