Skip to content

Commit

Permalink
refactor and add test case
Browse files Browse the repository at this point in the history
  • Loading branch information
sokra committed Oct 25, 2021
1 parent 537da95 commit ba154bc
Show file tree
Hide file tree
Showing 14 changed files with 431 additions and 187 deletions.
97 changes: 60 additions & 37 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1117,43 +1117,7 @@ export interface Experiments {
/**
* Compile entrypoints and import()s only when they are accessed.
*/
lazyCompilation?:
| boolean
| {
/**
* A custom backend.
*/
backend?:
| ((
compiler: import("../lib/Compiler"),
client: string,
callback: (err?: Error, api?: any) => void
) => void)
| ((
compiler: import("../lib/Compiler"),
client: string
) => Promise<any>);
/**
* Additional configuration to pass to the backend server.
*/
backendConfiguration?: import("../lib/hmr/lazyCompilationBackend").BackendConfiguration;
/**
* A custom client.
*/
client?: string;
/**
* Enable/disable lazy compilation for entries.
*/
entries?: boolean;
/**
* Enable/disable lazy compilation for import() modules.
*/
imports?: boolean;
/**
* Specify which entrypoints or import()ed modules should be lazily compiled. This is matched with the imported module and not the entrypoint name.
*/
test?: RegExp | string | ((module: import("../lib/Module")) => boolean);
};
lazyCompilation?: boolean | LazyCompilationOptions;
/**
* Allow output javascript files as module source type.
*/
Expand Down Expand Up @@ -1188,6 +1152,65 @@ export interface HttpUriOptions {
*/
upgrade?: boolean;
}
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "LazyCompilationOptions".
*/
export interface LazyCompilationOptions {
backend?:
| (
| ((
compiler: import("../lib/Compiler"),
callback: (
err?: Error,
api?: import("../lib/hmr/LazyCompilationPlugin").BackendApi
) => void
) => void)
| ((
compiler: import("../lib/Compiler")
) => Promise<import("../lib/hmr/LazyCompilationPlugin").BackendApi>)
)
| LazyCompilationDefaultBackendOptions;
/**
* Enable/disable lazy compilation for entries.
*/
entries?: boolean;
/**
* Enable/disable lazy compilation for import() modules.
*/
imports?: boolean;
/**
* Specify which entrypoints or import()ed modules should be lazily compiled. This is matched with the imported module and not the entrypoint name.
*/
test?: RegExp | string | ((module: import("../lib/Module")) => boolean);
}
/**
* Options for the default backend.
*/
export interface LazyCompilationDefaultBackendOptions {
/**
* A custom client.
*/
client?: string;
/**
* Specifies where to listen to from the server.
*/
listen?:
| number
| import("net").ListenOptions
| ((server: import("net").Server) => void);
/**
* Specifies the protocol the client should use to connect to the server.
*/
protocol?: "http" | "https";
/**
* Specifies how to create the server handling the EventSource requests.
*/
server?:
| (import("https").ServerOptions | import("http").ServerOptions)
| (() => import("net").Server);
[k: string]: any;
}
/**
* Enable presets of externals for specific targets.
*/
Expand Down
4 changes: 3 additions & 1 deletion hot/lazy-compilation-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ exports.keepAlive = function (options) {
var active = options.active;
var module = options.module;
var response;
var request = require("http").request(
var request = (
urlBase.startsWith("https") ? require("https") : require("http")
).request(
urlBase + data,
{
agent: false,
Expand Down
25 changes: 13 additions & 12 deletions lib/WebpackOptionsApply.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,21 @@ class WebpackOptionsApply extends OptionsApply {
: null;
new LazyCompilationPlugin({
backend:
(lazyOptions && lazyOptions.backend) ||
require("./hmr/lazyCompilationBackend"),
client:
(lazyOptions && lazyOptions.client) ||
require.resolve(
`../hot/lazy-compilation-${
options.externalsPresets.node ? "node" : "web"
}.js`
),
typeof lazyOptions.backend === "function"
? lazyOptions.backend
: require("./hmr/lazyCompilationBackend")({
...lazyOptions.backend,
client:
lazyOptions.backend.client ||
require.resolve(
`../hot/lazy-compilation-${
options.externalsPresets.node ? "node" : "web"
}.js`
)
}),
entries: !lazyOptions || lazyOptions.entries !== false,
imports: !lazyOptions || lazyOptions.imports !== false,
test: (lazyOptions && lazyOptions.test) || undefined,
backendConfiguration:
(lazyOptions && lazyOptions.backendConfiguration) || undefined
test: (lazyOptions && lazyOptions.test) || undefined
}).apply(compiler);
}

Expand Down
36 changes: 13 additions & 23 deletions lib/hmr/LazyCompilationPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ const { registerNotSerializable } = require("../util/serialization");
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */

/**
* @typedef {Object} BackendApi
* @property {function(Error=): void} dispose
* @property {function(Module): { client: string, data: string, active: boolean }} module
*/

const IGNORED_DEPENDENCY_TYPES = new Set([
"import.meta.webpackHot.accept",
"import.meta.webpackHot.decline",
Expand Down Expand Up @@ -303,27 +309,16 @@ class LazyCompilationDependencyFactory extends ModuleFactory {
class LazyCompilationPlugin {
/**
* @param {Object} options options
* @param {(function(Compiler, string, function(Error?, any?): void, Object): void) | function(Compiler, string): Promise<any>} options.backend the backend
* @param {Object} options.backendConfiguration additional configuration passed to backend
* @param {string} options.client the client reference
* @param {(function(Compiler, function(Error?, BackendApi?): void): void) | function(Compiler): Promise<BackendApi>} options.backend the backend
* @param {boolean} options.entries true, when entries are lazy compiled
* @param {boolean} options.imports true, when import() modules are lazy compiled
* @param {RegExp | string | (function(Module): boolean)} options.test additional filter for lazy compiled entrypoint modules
*/
constructor({
backend,
client,
entries,
imports,
test,
backendConfiguration
}) {
constructor({ backend, entries, imports, test }) {
this.backend = backend;
this.client = client;
this.entries = entries;
this.imports = imports;
this.test = test;
this.backendConfiguration = backendConfiguration;
}
/**
* Apply the plugin
Expand All @@ -336,16 +331,11 @@ class LazyCompilationPlugin {
"LazyCompilationPlugin",
(params, callback) => {
if (backend !== undefined) return callback();
const promise = this.backend(
compiler,
this.client,
(err, result) => {
if (err) return callback(err);
backend = result;
callback();
},
this.backendConfiguration
);
const promise = this.backend(compiler, (err, result) => {
if (err) return callback(err);
backend = result;
callback();
});
if (promise && promise.then) {
promise.then(b => {
backend = b;
Expand Down
53 changes: 31 additions & 22 deletions lib/hmr/lazyCompilationBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,43 @@

/** @typedef {import("http").ServerOptions} HttpServerOptions */
/** @typedef {import("https").ServerOptions} HttpsServerOptions */
/** @typedef {import("../../declarations/WebpackOptions").LazyCompilationDefaultBackendOptions} LazyCompilationDefaultBackendOptions */
/** @typedef {import("../Compiler")} Compiler */

/**
* @typedef {Object} BackendConfiguration
* @property {HttpServerOptions} httpServerOptions Options to be passed to the Node.js HTTP module.
* @property {HttpsServerOptions} httpsServerOptions Options to be passed to the Node.js HTTPS module.
* @property {number?} port Custom port for lazy compilation backend. If not defined, a random port will be used.
*/

/**
* @callback BackendHandler
* @param {Compiler} compiler compiler
* @param {string} client client reference
* @param {function(Error?, any?): void} callback callback
* @param {?BackendConfiguration} backendConfiguration additional options for the backend
* @returns {void}
*/
module.exports = (compiler, client, callback, backendConfiguration) => {

/**
* @param {Omit<LazyCompilationDefaultBackendOptions, "client"> & { client: NonNullable<LazyCompilationDefaultBackendOptions["client"]>}} options additional options for the backend
* @returns {BackendHandler} backend
*/
module.exports = options => (compiler, callback) => {
const logger = compiler.getInfrastructureLogger("LazyCompilationBackend");
const activeModules = new Map();
const prefix = "/lazy-compilation-using-";

const isHTTPS =
!!backendConfiguration && !!backendConfiguration.httpsServerOptions;
const isHttps =
options.protocol === "https" ||
(typeof options.server === "object" &&
("key" in options.server || "pfx" in options.server));

const createServer =
typeof options.server === "function"
? options.server
: (() => {
const http = isHttps ? require("https") : require("http");
return http.createServer.bind(http, options.server);
})();
const listen =
typeof options.listen === "function"
? options.listen
: server => server.listen(options.listen);

const protocol = isHTTPS ? "https" : "http";
const httpModule = isHTTPS ? require("https") : require("http");
const protocol = options.protocol || (isHttps ? "https" : "http");

const requestListener = (req, res) => {
const keys = req.url.slice(prefix.length).split("@");
Expand Down Expand Up @@ -67,12 +78,8 @@ module.exports = (compiler, client, callback, backendConfiguration) => {
if (moduleActivated && compiler.watching) compiler.watching.invalidate();
};

const server = httpModule.createServer(
backendConfiguration &&
(backendConfiguration.httpServerOptions ||
backendConfiguration.httpsServerOptions),
requestListener
);
const server = /** @type {import("net").Server} */ (createServer());
server.on("request", requestListener);

let isClosing = false;
/** @type {Set<import("net").Socket>} */
Expand All @@ -84,7 +91,8 @@ module.exports = (compiler, client, callback, backendConfiguration) => {
});
if (isClosing) socket.destroy();
});
server.listen(backendConfiguration && backendConfiguration.port, err => {
server.on("clientError", e => logger.warn(e));
server.on("listening", err => {
if (err) return callback(err);
const addr = server.address();
if (typeof addr === "string") throw new Error("addr must not be a string");
Expand Down Expand Up @@ -115,11 +123,12 @@ module.exports = (compiler, client, callback, backendConfiguration) => {
).replace(/%(2F|3A|24|26|2B|2C|3B|3D|3A)/g, decodeURIComponent)}`;
const active = activeModules.get(key) > 0;
return {
client: `${client}?${encodeURIComponent(urlBase + prefix)}`,
client: `${options.client}?${encodeURIComponent(urlBase + prefix)}`,
data: key,
active
};
}
});
});
listen(server);
};
2 changes: 1 addition & 1 deletion schemas/WebpackOptions.check.js

Large diffs are not rendered by default.

Loading

0 comments on commit ba154bc

Please sign in to comment.