From 27b1e17f5b87046f2040ac91bcdffaddd92ec3b1 Mon Sep 17 00:00:00 2001 From: ikopeykin Date: Wed, 10 Jul 2019 12:42:34 +0300 Subject: [PATCH 1/4] createHash typescript --- lib/Compilation.js | 8 ++-- lib/HashedModuleIdsPlugin.js | 4 +- lib/JavascriptModulesPlugin.js | 5 +-- lib/ModuleFilenameHelpers.js | 3 +- lib/NamedModulesPlugin.js | 3 +- lib/NormalModule.js | 2 +- lib/util/createHash.js | 72 ++++++++++++++++++++++++++++------ 7 files changed, 75 insertions(+), 22 deletions(-) diff --git a/lib/Compilation.js b/lib/Compilation.js index 80ef083e38f..a56b0a9c4e2 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -2294,7 +2294,7 @@ class Compilation extends Tapable { const module = modules[i]; const moduleHash = createHash(hashFunction); module.updateHash(moduleHash); - module.hash = moduleHash.digest(hashDigest); + module.hash = /** @type {string} */ (moduleHash.digest(hashDigest)); module.renderedHash = module.hash.substr(0, hashDigestLength); } // clone needed as sort below is inplace mutation @@ -2329,7 +2329,7 @@ class Compilation extends Tapable { this.dependencyTemplates ); this.hooks.chunkHash.call(chunk, chunkHash); - chunk.hash = chunkHash.digest(hashDigest); + chunk.hash = /** @type {string} */ (chunkHash.digest(hashDigest)); hash.update(chunk.hash); chunk.renderedHash = chunk.hash.substr(0, hashDigestLength); this.hooks.contentHash.call(chunk); @@ -2337,7 +2337,7 @@ class Compilation extends Tapable { this.errors.push(new ChunkRenderError(chunk, "", err)); } } - this.fullHash = hash.digest(hashDigest); + this.fullHash = /** @type {string} */ (hash.digest(hashDigest)); this.hash = this.fullHash.substr(0, hashDigestLength); } @@ -2353,7 +2353,7 @@ class Compilation extends Tapable { const hash = createHash(hashFunction); hash.update(this.fullHash); hash.update(update); - this.fullHash = hash.digest(hashDigest); + this.fullHash = /** @type {string} */ (hash.digest(hashDigest)); this.hash = this.fullHash.substr(0, hashDigestLength); } diff --git a/lib/HashedModuleIdsPlugin.js b/lib/HashedModuleIdsPlugin.js index 0c720c181b2..7a860f74f44 100644 --- a/lib/HashedModuleIdsPlugin.js +++ b/lib/HashedModuleIdsPlugin.js @@ -45,7 +45,9 @@ class HashedModuleIdsPlugin { }); const hash = createHash(options.hashFunction); hash.update(id); - const hashId = hash.digest(options.hashDigest); + const hashId = /** @type {string} */ (hash.digest( + options.hashDigest + )); let len = options.hashDigestLength; while (usedIds.has(hashId.substr(0, len))) len++; module.id = hashId.substr(0, len); diff --git a/lib/JavascriptModulesPlugin.js b/lib/JavascriptModulesPlugin.js index 07030c9a301..2c1bbe4d45d 100644 --- a/lib/JavascriptModulesPlugin.js +++ b/lib/JavascriptModulesPlugin.js @@ -148,9 +148,8 @@ class JavascriptModulesPlugin { hash.update(m.hash); } } - chunk.contentHash.javascript = hash - .digest(hashDigest) - .substr(0, hashDigestLength); + const digest = /** @type {string} */ (hash.digest(hashDigest)); + chunk.contentHash.javascript = digest.substr(0, hashDigestLength); }); } ); diff --git a/lib/ModuleFilenameHelpers.js b/lib/ModuleFilenameHelpers.js index 105e89e3fa8..bd0742b7f70 100644 --- a/lib/ModuleFilenameHelpers.js +++ b/lib/ModuleFilenameHelpers.js @@ -44,7 +44,8 @@ const getBefore = (str, token) => { const getHash = str => { const hash = createHash("md4"); hash.update(str); - return hash.digest("hex").substr(0, 4); + const digest = /** @type {string} */ (hash.digest("hex")); + return digest.substr(0, 4); }; const asRegExp = test => { diff --git a/lib/NamedModulesPlugin.js b/lib/NamedModulesPlugin.js index a3857ac8e90..2d84aafe297 100644 --- a/lib/NamedModulesPlugin.js +++ b/lib/NamedModulesPlugin.js @@ -10,7 +10,8 @@ const RequestShortener = require("./RequestShortener"); const getHash = str => { const hash = createHash("md4"); hash.update(str); - return hash.digest("hex").substr(0, 4); + const digest = /** @type {string} */ (hash.digest("hex")); + return digest.substr(0, 4); }; class NamedModulesPlugin { diff --git a/lib/NormalModule.js b/lib/NormalModule.js index ec54e07c843..ba8d31819f2 100644 --- a/lib/NormalModule.js +++ b/lib/NormalModule.js @@ -405,7 +405,7 @@ class NormalModule extends Module { } hash.update("meta"); hash.update(JSON.stringify(this.buildMeta)); - this._buildHash = hash.digest("hex"); + this._buildHash = /** @type {string} */ (hash.digest("hex")); } build(options, compilation, resolver, fs, callback) { diff --git a/lib/util/createHash.js b/lib/util/createHash.js index fa18ffd8d0d..bd514eaca0f 100644 --- a/lib/util/createHash.js +++ b/lib/util/createHash.js @@ -4,21 +4,51 @@ */ "use strict"; -/** @typedef {{new(): Hash}} HashConstructor */ -/** - * @typedef {Object} Hash - * @property {function(string|Buffer, string=): Hash} update - * @property {function(string): string} digest - */ - const BULK_SIZE = 1000; -class BulkUpdateDecorator { +class BaseHash { + /** + * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} + * @param {string|Buffer} data data + * @param {string} [inputEncoding] data encoding + * @returns {BaseHash} updated hash + */ + update(data, inputEncoding) { + return this; + } + + /** + * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} + * @param {string} [encoding] encoding of the return value + * @returns {string|Buffer} digest + */ + digest(encoding) { + return ""; + } +} + +/** @typedef {BaseHash} Hash */ +/** @typedef {{new(): BaseHash}} HashConstructor */ + +/** + * @extends {BaseHash} + */ +class BulkUpdateDecorator extends BaseHash { + /** + * @param {BaseHash} hash hash + */ constructor(hash) { + super(); this.hash = hash; this.buffer = ""; } + /** + * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} + * @param {string|Buffer} data data + * @param {string} [inputEncoding] data encoding + * @returns {BaseHash} updated hash + */ update(data, inputEncoding) { if ( inputEncoding !== undefined || @@ -40,6 +70,11 @@ class BulkUpdateDecorator { return this; } + /** + * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} + * @param {string} [encoding] encoding of the return value + * @returns {string|Buffer} digest + */ digest(encoding) { if (this.buffer.length > 0) { this.hash.update(this.buffer); @@ -51,18 +86,33 @@ class BulkUpdateDecorator { } } -/* istanbul ignore next */ -class DebugHash { +/** + * istanbul ignore next + * @extends {BaseHash} + */ +class DebugHash extends BaseHash { constructor() { + super(); this.string = ""; } + /** + * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} + * @param {string|Buffer} data data + * @param {string} [inputEncoding] data encoding + * @returns {BaseHash} updated hash + */ update(data, inputEncoding) { if (typeof data !== "string") data = data.toString("utf-8"); this.string += data; return this; } + /** + * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} + * @param {string} [encoding] encoding of the return value + * @returns {string|Buffer} digest + */ digest(encoding) { return this.string.replace(/[^a-z0-9]+/gi, m => Buffer.from(m).toString("hex") @@ -73,7 +123,7 @@ class DebugHash { /** * Creates a hash by name or function * @param {string | HashConstructor} algorithm the algorithm name or a constructor creating a hash - * @returns {Hash} the hash + * @returns {BaseHash} the hash */ module.exports = algorithm => { if (typeof algorithm === "function") { From a223ffdcc09367ac63a25763052d1ce6bb79668a Mon Sep 17 00:00:00 2001 From: ikopeykin Date: Wed, 10 Jul 2019 14:16:09 +0300 Subject: [PATCH 2/4] fix typescript for HashContructor --- lib/util/createHash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/createHash.js b/lib/util/createHash.js index bd514eaca0f..97f4ca49c59 100644 --- a/lib/util/createHash.js +++ b/lib/util/createHash.js @@ -28,7 +28,7 @@ class BaseHash { } /** @typedef {BaseHash} Hash */ -/** @typedef {{new(): BaseHash}} HashConstructor */ +/** @typedef {typeof BaseHash} HashConstructor */ /** * @extends {BaseHash} From bd7d95bfc14c376147255f6dce062e70bb00587f Mon Sep 17 00:00:00 2001 From: ikopeykin Date: Mon, 15 Jul 2019 14:17:12 +0300 Subject: [PATCH 3/4] #9391 resolve discussions, AbstractMethodError --- declarations/WebpackOptions.d.ts | 2 +- lib/AbstractMethodError.js | 36 ++++++++++++++++++++++++ lib/util/createHash.js | 42 +++++++++++++--------------- schemas/WebpackOptions.json | 2 +- test/AbstractMethodError.unittest.js | 27 ++++++++++++++++++ 5 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 lib/AbstractMethodError.js create mode 100644 test/AbstractMethodError.unittest.js diff --git a/declarations/WebpackOptions.d.ts b/declarations/WebpackOptions.d.ts index bba927cd26e..617bf45136d 100644 --- a/declarations/WebpackOptions.d.ts +++ b/declarations/WebpackOptions.d.ts @@ -1094,7 +1094,7 @@ export interface OutputOptions { /** * Algorithm used for generation the hash (see node.js crypto package) */ - hashFunction?: string | (new () => import("../lib/util/createHash").Hash); + hashFunction?: string | import("../lib/util/createHash").HashConstructor; /** * Any string which is added to the hash to salt it */ diff --git a/lib/AbstractMethodError.js b/lib/AbstractMethodError.js new file mode 100644 index 00000000000..49c06ee5466 --- /dev/null +++ b/lib/AbstractMethodError.js @@ -0,0 +1,36 @@ +"use strict"; + +const WebpackError = require("./WebpackError"); +const CURRENT_METHOD_REGEXP = /at ([a-zA-Z0-9_.]*)/; + +/** + * @param {string=} method method name + * @returns {string} message + */ +function createMessage(method) { + return `Abstract method${method ? " " + method : ""}. Must be overriden.`; +} + +/** + * Error for abstract method + * @example + * class FooClass { + * abstractMethod() { + * throw new AbstractMethodError(); // error message: Abstract method FooClass.abstractMethod. Must be overriden. + * } + * } + * + */ +class AbstractMethodError extends WebpackError { + constructor() { + super(createMessage()); + this.name = "AbstractMethodError"; + /** @type {RegExpMatchArray} */ + const match = this.stack.split("\n")[1].match(CURRENT_METHOD_REGEXP); + if (match && match[1]) { + this.message = createMessage(match[1]); + } + } +} + +module.exports = AbstractMethodError; diff --git a/lib/util/createHash.js b/lib/util/createHash.js index 97f4ca49c59..64de510da5c 100644 --- a/lib/util/createHash.js +++ b/lib/util/createHash.js @@ -4,38 +4,37 @@ */ "use strict"; +const AbstractMethodError = require("../AbstractMethodError"); + const BULK_SIZE = 1000; -class BaseHash { +class Hash { /** * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} * @param {string|Buffer} data data - * @param {string} [inputEncoding] data encoding - * @returns {BaseHash} updated hash + * @param {string=} inputEncoding data encoding + * @returns {this} updated hash */ update(data, inputEncoding) { - return this; + throw new AbstractMethodError(); } /** * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} - * @param {string} [encoding] encoding of the return value + * @param {string=} encoding encoding of the return value * @returns {string|Buffer} digest */ digest(encoding) { - return ""; + throw new AbstractMethodError(); } } -/** @typedef {BaseHash} Hash */ -/** @typedef {typeof BaseHash} HashConstructor */ +exports.Hash = Hash; +/** @typedef {typeof Hash} HashConstructor */ -/** - * @extends {BaseHash} - */ -class BulkUpdateDecorator extends BaseHash { +class BulkUpdateDecorator extends Hash { /** - * @param {BaseHash} hash hash + * @param {Hash} hash hash */ constructor(hash) { super(); @@ -46,8 +45,8 @@ class BulkUpdateDecorator extends BaseHash { /** * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} * @param {string|Buffer} data data - * @param {string} [inputEncoding] data encoding - * @returns {BaseHash} updated hash + * @param {string=} inputEncoding data encoding + * @returns {this} updated hash */ update(data, inputEncoding) { if ( @@ -72,7 +71,7 @@ class BulkUpdateDecorator extends BaseHash { /** * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} - * @param {string} [encoding] encoding of the return value + * @param {string=} encoding encoding of the return value * @returns {string|Buffer} digest */ digest(encoding) { @@ -88,9 +87,8 @@ class BulkUpdateDecorator extends BaseHash { /** * istanbul ignore next - * @extends {BaseHash} */ -class DebugHash extends BaseHash { +class DebugHash extends Hash { constructor() { super(); this.string = ""; @@ -99,8 +97,8 @@ class DebugHash extends BaseHash { /** * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} * @param {string|Buffer} data data - * @param {string} [inputEncoding] data encoding - * @returns {BaseHash} updated hash + * @param {string=} inputEncoding data encoding + * @returns {this} updated hash */ update(data, inputEncoding) { if (typeof data !== "string") data = data.toString("utf-8"); @@ -110,7 +108,7 @@ class DebugHash extends BaseHash { /** * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} - * @param {string} [encoding] encoding of the return value + * @param {string=} encoding encoding of the return value * @returns {string|Buffer} digest */ digest(encoding) { @@ -123,7 +121,7 @@ class DebugHash extends BaseHash { /** * Creates a hash by name or function * @param {string | HashConstructor} algorithm the algorithm name or a constructor creating a hash - * @returns {BaseHash} the hash + * @returns {Hash} the hash */ module.exports = algorithm => { if (typeof algorithm === "function") { diff --git a/schemas/WebpackOptions.json b/schemas/WebpackOptions.json index daafe600438..81ad909373a 100644 --- a/schemas/WebpackOptions.json +++ b/schemas/WebpackOptions.json @@ -905,7 +905,7 @@ }, { "instanceof": "Function", - "tsType": "(new () => import('../lib/util/createHash').Hash)" + "tsType": "import('../lib/util/createHash').HashConstructor" } ] }, diff --git a/test/AbstractMethodError.unittest.js b/test/AbstractMethodError.unittest.js new file mode 100644 index 00000000000..6b42ec87389 --- /dev/null +++ b/test/AbstractMethodError.unittest.js @@ -0,0 +1,27 @@ +"use strict"; + +const AbstractMethodError = require("../lib/AbstractMethodError"); + +describe("WebpackError", () => { + class Foo { + abstractMethod() { + return new AbstractMethodError(); + } + } + + class Child extends Foo {} + + const expectedMessage = "Abstract method $1. Must be overriden."; + + it("Should construct message with caller info", () => { + const fooClassError = new Foo().abstractMethod(); + const childClassError = new Child().abstractMethod(); + + expect(fooClassError.message).toBe( + expectedMessage.replace("$1", "Foo.abstractMethod") + ); + expect(childClassError.message).toBe( + expectedMessage.replace("$1", "Child.abstractMethod") + ); + }); +}); From bf1a24a9ab10e6d9b2240d41d90f86de9877ee19 Mon Sep 17 00:00:00 2001 From: ikopeykin Date: Mon, 15 Jul 2019 17:19:51 +0300 Subject: [PATCH 4/4] #9391 resolve super call discussion --- .eslintrc.js | 3 ++- lib/AbstractMethodError.js | 21 ++++++++++++++------- test/AbstractMethodError.unittest.js | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c971aa16e35..d2aaef6b9ed 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -71,7 +71,8 @@ module.exports = { }; return acc; }, {})), - extends: "extends" + extends: "extends", + constructor: "constructor" } } }, diff --git a/lib/AbstractMethodError.js b/lib/AbstractMethodError.js index 49c06ee5466..c9eb9cffb5a 100644 --- a/lib/AbstractMethodError.js +++ b/lib/AbstractMethodError.js @@ -8,7 +8,19 @@ const CURRENT_METHOD_REGEXP = /at ([a-zA-Z0-9_.]*)/; * @returns {string} message */ function createMessage(method) { - return `Abstract method${method ? " " + method : ""}. Must be overriden.`; + return `Abstract method${method ? " " + method : ""}. Must be overridden.`; +} + +/** + * @constructor + */ +function Message() { + this.stack = undefined; + Error.captureStackTrace(this); + /** @type {RegExpMatchArray} */ + const match = this.stack.split("\n")[3].match(CURRENT_METHOD_REGEXP); + + this.message = match && match[1] ? createMessage(match[1]) : createMessage(); } /** @@ -23,13 +35,8 @@ function createMessage(method) { */ class AbstractMethodError extends WebpackError { constructor() { - super(createMessage()); + super(new Message().message); this.name = "AbstractMethodError"; - /** @type {RegExpMatchArray} */ - const match = this.stack.split("\n")[1].match(CURRENT_METHOD_REGEXP); - if (match && match[1]) { - this.message = createMessage(match[1]); - } } } diff --git a/test/AbstractMethodError.unittest.js b/test/AbstractMethodError.unittest.js index 6b42ec87389..862a2860409 100644 --- a/test/AbstractMethodError.unittest.js +++ b/test/AbstractMethodError.unittest.js @@ -11,7 +11,7 @@ describe("WebpackError", () => { class Child extends Foo {} - const expectedMessage = "Abstract method $1. Must be overriden."; + const expectedMessage = "Abstract method $1. Must be overridden."; it("Should construct message with caller info", () => { const fooClassError = new Foo().abstractMethod();