html-render-webpack-plugin 0.0.0-master-20261021073115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2018 Jahred Hope
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,311 @@
1
+ # html-render-webpack-plugin
2
+
3
+ [![npm](https://img.shields.io/npm/v/html-render-webpack-plugin.svg)](https://www.npmjs.com/package/html-render-webpack-plugin)
4
+
5
+ Plugin to create HTML files with JavaScript.
6
+
7
+ - Supports [multiple configurations](https://webpack.js.org/configuration/configuration-types/#exporting-multiple-configurations)
8
+ - Supports [code-splitting](https://webpack.js.org/guides/code-splitting/) and [dynamic imports](https://webpack.js.org/guides/code-splitting/#dynamic-imports)
9
+
10
+ # Setup
11
+
12
+ ```bash
13
+ $ npm install webpack html-render-webpack-plugin
14
+ # OR
15
+ $ pnpm add webpack html-render-webpack-plugin
16
+ ```
17
+
18
+ **webpack.config.js**
19
+
20
+ ```js
21
+ module.exports = {
22
+ ...,
23
+ plugins: [new HtmlRenderPlugin().rendererPlugin]
24
+ };
25
+ ```
26
+
27
+ # Multiple configurations
28
+
29
+ If you use [multiple webpack configurations](https://webpack.js.org/configuration/configuration-types/#exporting-multiple-configurations) you may want to add information from other builds when rendering.
30
+
31
+ For example, you may wish to add a script tag where the name includes a hash. The asset name comes from the output of one build (browser assets) whilst the render is performed in another build (node rendering).
32
+
33
+ **src/render.js**
34
+
35
+ ```js
36
+ export default ({ assetsByChunkName }) => {
37
+ return `<html>
38
+ <body>
39
+ <script src="${assetsByChunkName.main}"></script>
40
+ </body>
41
+ </html>`;
42
+ };
43
+ ```
44
+
45
+ **dist/index.html**
46
+
47
+ ```html
48
+ <html>
49
+ <body>
50
+ <script src="/main-daf2166db871ad045ea4.js"></script>
51
+ </body>
52
+ </html>
53
+ ```
54
+
55
+ See [the full example below](#example-client-assets).
56
+
57
+ ## Multiple configuration setup
58
+
59
+ Add `htmlRenderPlugin.statsCollectorPlugin` to the plugins of all configurations you want to get stats for.
60
+
61
+ Add `htmlRenderPlugin.rendererPlugin` to the plugin of the configuration you want to use to render html.
62
+
63
+ HtmlRenderPlugin will then pass the [Webpack Stats](https://webpack.js.org/api/stats/) for those builds into your render function.
64
+
65
+ **webpack.config.js**
66
+
67
+ ```js
68
+ const { HtmlRenderPlugin } = require("html-render-webpack-plugin");
69
+
70
+ const htmlRenderPlugin = new HtmlRenderPlugin();
71
+ module.exports = [
72
+ {
73
+ name: "render",
74
+ target: "node", // Creates assets that render HTML that runs well in node
75
+ plugins: [htmlRenderPlugin.rendererPlugin],
76
+ },
77
+ {
78
+ name: "client",
79
+ target: "web", // Creates files that run on the browser
80
+ plugins: [htmlRenderPlugin.statsCollectorPlugin],
81
+ },
82
+ ];
83
+ ```
84
+
85
+ See [examples](#examples) for more details.
86
+
87
+ # Options
88
+
89
+ ## Option: renderEntry _string_
90
+
91
+ **default:** "main"
92
+
93
+ The [webpack entry](https://webpack.js.org/concepts/entry-points/) to use when rendering. Override when using [object syntax](https://webpack.js.org/concepts/entry-points/#object-syntax).
94
+
95
+ **webpack.config.js**
96
+
97
+ ```js
98
+ module.exports = {
99
+ entry: {
100
+ myRender: "./src/myRender.js",
101
+ },
102
+ plugins: [
103
+ new HtmlRenderPlugin({
104
+ renderEntry: "myRender",
105
+ }).render,
106
+ ],
107
+ };
108
+ ```
109
+
110
+ ## Option: renderDirectory _string_
111
+
112
+ The location to create rendered files. Defaults to the rendered assets output.
113
+
114
+ Useful when deploying HTML files separate to other build assets.
115
+
116
+ ## Option: routes _Array<object|string>_
117
+
118
+ **default:** `[""]`
119
+
120
+ Renders a HTML page for each value in the array.
121
+ A route can be a string showing the folder or file to render, or an object containing a route parameter. `index.html` is automatically appended for paths.
122
+
123
+ ```js
124
+ const routes = ["", "contact", "about"];
125
+ ```
126
+
127
+ A route can be an object, containing a `route` parameter.
128
+
129
+ ```js
130
+ const routes = [
131
+ {
132
+ route: "en-us/contact",
133
+ language: "en-us",
134
+ },
135
+ {
136
+ route: "en-us/about",
137
+ language: "en-us",
138
+ },
139
+ {
140
+ route: "en-au/contact",
141
+ language: "en-au",
142
+ },
143
+ {
144
+ route: "/en-au/about",
145
+ language: "en-au",
146
+ },
147
+ ];
148
+ ```
149
+
150
+ ## Option: mapStatsToParams _Function_
151
+
152
+ **default:** `({webpackStats, ...route}) => ({ webpackStats })`
153
+
154
+ mapStatsToParams should accept webpackStats and [route](#option-routes-arrayobjectstring) information and returns values to be passed into render.
155
+ The function is called individually for each render.
156
+
157
+ **Recommendation:** mapStatsToParams is an opportunity to limit what information is provided to your render function. Keeping the boundary between your build code and application code simple. Avoid passing all webpackStats into your render function, pull out only the information needed. It is recommended to override the default mapStatsToParams behaviour.
158
+
159
+ ## Option: transformFilePath _Function_
160
+
161
+ **default:** `(route) => route.route ? route.route : route`
162
+
163
+ By default a file will be created using the `route` value.
164
+ For example the value `{route: '/about/us'}` would create **about/us/index.html**
165
+
166
+ If you want to use a different file path you can provide a `transformFilePath` function.
167
+
168
+ ```js
169
+ new HtmlRenderPlugin({
170
+ ...
171
+ transformFilePath: ({ route, language }) => `${language}/${route}`
172
+ routes: [
173
+ { route: '/about/us', language: 'en-us' },
174
+ { route: '/about/us', language: 'en-au' }
175
+ ]
176
+ });
177
+ ```
178
+
179
+ In this example, the resulting files will be
180
+
181
+ - **en-us/about/us/index.html**
182
+
183
+ - **en-au/about/us/index.html**
184
+
185
+ ## Option: renderConcurrency _string ("serial"|"parallel")_
186
+
187
+ **default:** `"serial"`
188
+
189
+ By default each file will be rendered one after the other, creating a simple sequential build log. When renders with significant asynchronous work you may want to have each render run in parallel.
190
+
191
+ ```
192
+ new HtmlRenderPlugin({
193
+ renderConcurrency: 'parallel'
194
+ });
195
+ ```
196
+
197
+ ## Option: skipAssets _Function_
198
+
199
+ **default:** `false`
200
+
201
+ After waiting for all builds to complete HtmlRenderPlugin will render all routes. For particularly large projects with a large number of routes this can take some time. For watch builds you may want to skip emitting assets, relying on `createDevRouter` instead.
202
+
203
+ ## Option: transformExpressPath _Function_
204
+
205
+ **default:** `(route) => route.route ? route.route : route`
206
+
207
+ When creating a dev router each route will be attached to the router using it's `route` value.
208
+
209
+ If you want to use a different express route you can provide a `transformExpressPath` function.
210
+
211
+ # Dev Server
212
+
213
+ Create an [Express Middleware](https://expressjs.com/en/guide/using-middleware.html) to attach to Webpack Dev Server to speed up development builds.
214
+
215
+ For particularly large projects with slow renders and a large number of routes rendering every route on every build can slow down development. The dev server allows you to only render the pages as they are needed during development, whilst ensuring the resulting render works like the full production render.
216
+
217
+ Using the [Webpack Dev Server Node API](https://github.com/webpack/webpack-dev-server/blob/master/examples/api/simple/server.js#L14) create a dev server and attach the dev HtmlRenderPlugin router to it. When pages are requested they will be rendered just-in-time, using the same method of rendering as production.
218
+
219
+ ```js
220
+ const htmlRenderPlugin = new HtmlRenderPlugin({
221
+ routes,
222
+ skipAssets: true,
223
+ });
224
+
225
+ const compiler = webpack(config);
226
+
227
+ const webpackDevServer = new WebpackDevServer(compiler, {
228
+ before: (app) => {
229
+ app.use(htmlRenderPlugin.createDevRouter());
230
+ },
231
+ });
232
+
233
+ webpackDevServer.listen("8081");
234
+ ```
235
+
236
+ **Note:** Ensure that you use the same htmlRenderPlugin created for your webpack configuration as you use for your dev server.
237
+
238
+ # Manual just-in-time rendering
239
+
240
+ As an alternative to using the default dev server you can access `renderWhenReady` to apply your own just-in-time rendering.
241
+
242
+ Just call `renderWhenReady` with any route, and the next time the renderer is ready the render will be performed.
243
+
244
+ **Note:** Be careful to only use routes that are generated in a production. Not doing this can lead to differences between development and production builds.
245
+
246
+ Example: Using an Express App to render a dynamic path with ids.
247
+
248
+ ```js
249
+ app.get('/books/:id', (req, res) => {
250
+ res.send(await htmlRenderPlugin.renderWhenReady({route: '/books/_id'}))
251
+ })
252
+ ```
253
+
254
+ Errors returned during this render will contain a `webpackStats` attribute when available. This can be useful when rendering your own error pages.
255
+
256
+ # Examples
257
+
258
+ ## Example: Client assets
259
+
260
+ An example of using `mapStatsToParams` to create `<script>` tags.
261
+
262
+ **src/render.js**
263
+
264
+ ```js
265
+ export default ({ mainChunk }) => {
266
+ return `<html>
267
+ <body>
268
+ <script src="${mainChunk}"></script>
269
+ </body>
270
+ </html>`;
271
+ };
272
+ ```
273
+
274
+ **webpack.config.js**
275
+
276
+ ```js
277
+ const path = require("path");
278
+
279
+ const { htmlRenderPlugin, htmlRenderClientPlugin } = createHtmlRenderPlugin({
280
+ mapStatsToParams: ({ webpackStats }) => ({
281
+ mainChunk: webpackStats.toJson().assetsByChunkName.main,
282
+ }),
283
+ });
284
+
285
+ module.exports = [
286
+ {
287
+ name: "client",
288
+ target: "web",
289
+ output: {
290
+ filename: "client-[name]-[contenthash].js",
291
+ },
292
+ entry: path.resolve("src", "client.js"),
293
+ plugins: [htmlRenderPlugin.statsCollectorPlugin],
294
+ },
295
+ {
296
+ name: "render",
297
+ target: "node",
298
+ output: {
299
+ libraryExport: "default",
300
+ libraryTarget: "umd2",
301
+ filename: "render-[name]-[contenthash].js",
302
+ },
303
+ entry: path.resolve("src", "render.js"),
304
+ plugins: [htmlRenderPlugin.rendererPlugin],
305
+ },
306
+ ];
307
+ ```
308
+
309
+ # Migration Guides
310
+
311
+ Migration from v1 to v2? Checkout the [Migration Guide](./MIGRATION_GUIDE_v1_to_v2.md).
@@ -0,0 +1,3 @@
1
+ export default class RenderError extends Error {
2
+ constructor(error: Error);
3
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class RenderError extends Error {
4
+ constructor(error) {
5
+ super(`html-render-webpack-plugin: An error occurred during render: \n${error}`);
6
+ this.name = "RenderError";
7
+ Error.captureStackTrace(this, this.constructor);
8
+ }
9
+ }
10
+ exports.default = RenderError;
11
+ //# sourceMappingURL=RenderError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderError.js","sourceRoot":"","sources":["../src/RenderError.ts"],"names":[],"mappings":";;AAAA,MAAqB,WAAY,SAAQ,KAAK;IAC5C,YAAY,KAAY;QACtB,KAAK,CACH,kEAAkE,KAAK,EAAE,CAC1E,CAAC;QAEF,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAE1B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;CACF;AAVD,8BAUC"}
@@ -0,0 +1,23 @@
1
+ import { Stats } from "webpack";
2
+ import { Request } from "express";
3
+ export type WebpackStats = Stats;
4
+ export type BaseRoute = {
5
+ route: string;
6
+ };
7
+ export type Render<Route = BaseRoute> = (route: Route) => Promise<string>;
8
+ export type OnRendererReady<Route = BaseRoute> = (callback: (render: Render<Route>) => Promise<void>) => void;
9
+ export type Renderer = (...params: any[]) => Promise<string>;
10
+ export type SourceModules = Record<string, string>;
11
+ export type RouteInput<Route = BaseRoute> = Route | string;
12
+ export type Trace = (...values: any[]) => void;
13
+ export type Specifier = string;
14
+ export type ExtraGlobals = Record<string, unknown>;
15
+ export type RenderConcurrency = "parallel" | "serial";
16
+ export type MapStatsToParams = ({ webpackStats, }: {
17
+ webpackStats: WebpackStats;
18
+ }) => Record<string, unknown>;
19
+ export type TransformPath<Route = BaseRoute> = (route: Route) => string;
20
+ export type TransformExpressPath<Route = BaseRoute> = (route: Route) => string;
21
+ export type GetRouteFromRequest<Route> = (req: Request, routes: Route[]) => Route;
22
+ import originalFs from "fs";
23
+ export type FileSystem = typeof originalFs;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=common-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common-types.js","sourceRoot":"","sources":["../src/common-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import { Router } from "express";
2
+ import { OnRendererReady, BaseRoute, WebpackStats, TransformExpressPath, GetRouteFromRequest } from "./common-types";
3
+ interface Params<Route> {
4
+ routes: Route[];
5
+ getRouteFromRequest?: GetRouteFromRequest<Route>;
6
+ onRendererReady: OnRendererReady<Route>;
7
+ transformExpressPath: TransformExpressPath<Route>;
8
+ getClientStats: () => WebpackStats;
9
+ }
10
+ declare const _default: <Route extends BaseRoute>({ routes, getRouteFromRequest, onRendererReady, transformExpressPath, getClientStats, }: Params<Route>) => Router;
11
+ export = _default;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ const express_1 = __importDefault(require("express"));
15
+ const logging_1 = require("./logging");
16
+ const exception_formatter_1 = __importDefault(require("exception-formatter"));
17
+ module.exports = ({ routes, getRouteFromRequest, onRendererReady, transformExpressPath, getClientStats, }) => {
18
+ (0, logging_1.log)("Create dev server");
19
+ const formatErrorResponse = (error, webpackStats) => {
20
+ let devServerScripts = [];
21
+ if ("entrypoints" in webpackStats && webpackStats.entrypoints) {
22
+ try {
23
+ const devServerAssets = webpackStats.entrypoints.main.assets;
24
+ devServerScripts = devServerAssets.map((asset) => {
25
+ // webpack 5 reports assets as objects; older stats used strings.
26
+ const assetName = typeof asset === "string" ? asset : asset.name;
27
+ return `<script src="${webpackStats.publicPath}${assetName}"></script>`;
28
+ });
29
+ }
30
+ catch (err) {
31
+ console.error("Unable to load Dev Server Scripts. Error: ", err);
32
+ }
33
+ }
34
+ return [error, ...devServerScripts].join("\n");
35
+ };
36
+ const devServerRouter = express_1.default.Router();
37
+ const routesByExpressPath = {};
38
+ // Deduplicate paths to avoid duplicated processing in Express
39
+ routes.forEach((route) => {
40
+ const expressPath = transformExpressPath(route);
41
+ if (expressPath) {
42
+ routesByExpressPath[expressPath] = route;
43
+ }
44
+ });
45
+ Object.entries(routesByExpressPath).forEach(([expressPath, defaultRoute]) => {
46
+ devServerRouter.get(expressPath, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
47
+ onRendererReady((render) => __awaiter(void 0, void 0, void 0, function* () {
48
+ const route = getRouteFromRequest
49
+ ? getRouteFromRequest(req, routes)
50
+ : defaultRoute;
51
+ if (!route) {
52
+ next();
53
+ return;
54
+ }
55
+ if (!routes.includes(route)) {
56
+ const errorMessage = "Returned route was not an existing route. Ensure return value from getRouteFromRequest is route";
57
+ (0, logging_1.logError)(errorMessage);
58
+ next(new Error(errorMessage));
59
+ return;
60
+ }
61
+ (0, logging_1.log)(`Static render for ${route} from ${req.path}`);
62
+ try {
63
+ res.send(yield render(route));
64
+ }
65
+ catch (error) {
66
+ res.status(500).send(formatErrorResponse((0, exception_formatter_1.default)(error, {
67
+ format: "html",
68
+ inlineStyle: true,
69
+ basepath: "webpack://static/./",
70
+ }), getClientStats().toJson()));
71
+ }
72
+ }));
73
+ }));
74
+ });
75
+ return devServerRouter;
76
+ };
77
+ //# sourceMappingURL=createDevRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDevRouter.js","sourceRoot":"","sources":["../src/createDevRouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { FileSystem, ExtraGlobals } from "./common-types";
2
+ declare const _default: ({ fileName, fileSystem, rootDir, extraGlobals, }: {
3
+ fileName: string;
4
+ fileSystem: FileSystem;
5
+ extraGlobals: ExtraGlobals;
6
+ rootDir: string;
7
+ }) => import("./common-types").Renderer;
8
+ export = _default;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const evaluateFromFileSystem_1 = __importDefault(require("./evaluateFromFileSystem"));
6
+ module.exports = function createRenderer({ fileName, fileSystem, rootDir, extraGlobals, }) {
7
+ if (!fileName) {
8
+ throw new Error("Missing filename");
9
+ }
10
+ if (!fileSystem) {
11
+ throw new Error("Missing source");
12
+ }
13
+ if (!rootDir || typeof rootDir !== "string") {
14
+ throw new Error(`Recieved rootDir as ${typeof rootDir}`);
15
+ }
16
+ return (0, evaluateFromFileSystem_1.default)(fileName, fileSystem, rootDir, extraGlobals);
17
+ };
18
+ //# sourceMappingURL=createRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRenderer.js","sourceRoot":"","sources":["../src/createRenderer.ts"],"names":[],"mappings":";;;;AAAA,sFAA8D;AAG9D,iBAAS,SAAS,cAAc,CAAC,EAC/B,QAAQ,EACR,UAAU,EACV,OAAO,EACP,YAAY,GAMb;IACC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,IAAA,gCAAsB,EAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAC7E,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ExtraGlobals, FileSystem, Renderer } from "./common-types";
2
+ declare function evaluateFromFileSystem(specifier: string, fsModule: FileSystem, rootDir: string, extraGlobals: ExtraGlobals): Renderer;
3
+ export = evaluateFromFileSystem;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const path_1 = __importDefault(require("path"));
6
+ const node_vm_1 = require("node:vm");
7
+ const logging_1 = require("./logging");
8
+ // The wrapper Node uses for CommonJS modules. `extraGlobals` are appended as
9
+ // further parameters so module assets can read them as free variables.
10
+ const moduleParameters = [
11
+ "exports",
12
+ "require",
13
+ "module",
14
+ "__filename",
15
+ "__dirname",
16
+ ];
17
+ const identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
18
+ function getFromSourceModules(specifier, fsModule, rootDir) {
19
+ const sourceModuleSpecifier = specifier.replace(/^\.\//, "");
20
+ if (!fsModule.existsSync(path_1.default.resolve(rootDir, sourceModuleSpecifier))) {
21
+ (0, logging_1.log)(`Unable to find file specifier ${sourceModuleSpecifier}. Root: ${rootDir}.`);
22
+ return undefined;
23
+ }
24
+ return fsModule.readFileSync(path_1.default.resolve(rootDir, sourceModuleSpecifier));
25
+ }
26
+ function getExtraGlobalNames(extraGlobals) {
27
+ const names = Object.keys(extraGlobals || {});
28
+ for (const name of names) {
29
+ if (moduleParameters.includes(name)) {
30
+ throw new Error(`extraGlobals cannot use the reserved name "${name}".`);
31
+ }
32
+ if (!identifierPattern.test(name)) {
33
+ throw new Error(`extraGlobals key "${name}" is not a valid JavaScript identifier.`);
34
+ }
35
+ }
36
+ return names;
37
+ }
38
+ function evaluateFromFileSystem(specifier, fsModule, rootDir, extraGlobals) {
39
+ (0, logging_1.log)(`Evaluating source for ${specifier}". From root directory: "${rootDir}".`);
40
+ let source;
41
+ try {
42
+ source = getFromSourceModules(specifier, fsModule, rootDir);
43
+ }
44
+ catch (error) {
45
+ throw new Error(`An error reading "${specifier}". Error: ${error}`, {
46
+ cause: error,
47
+ });
48
+ }
49
+ if (!source) {
50
+ throw new Error(`Unable to find source for "${specifier}".`);
51
+ }
52
+ const filename = path_1.default.resolve(rootDir, specifier);
53
+ const extraGlobalNames = getExtraGlobalNames(extraGlobals);
54
+ const requireFn = createLinker(specifier, fsModule, rootDir, extraGlobals);
55
+ const module = {
56
+ exports: {},
57
+ id: filename,
58
+ filename,
59
+ loaded: false,
60
+ require: requireFn,
61
+ };
62
+ // Compiling in the current context keeps Node's globals (Buffer, process,
63
+ // console, ...) and built-in object identities intact, so values crossing
64
+ // into the module still satisfy `instanceof` checks.
65
+ const compiled = (0, node_vm_1.compileFunction)(source.toString(), [...moduleParameters, ...extraGlobalNames], { filename });
66
+ compiled.call(module.exports, module.exports, requireFn, module, filename, path_1.default.dirname(filename), ...extraGlobalNames.map((name) => extraGlobals[name]));
67
+ module.loaded = true;
68
+ return module.exports;
69
+ }
70
+ function createLinker(parentModulePath, fsModule, rootDir, extraGlobals) {
71
+ (0, logging_1.log)("Creating linker for", parentModulePath);
72
+ return function linker(specifier) {
73
+ const absPath = path_1.default.join(path_1.default.dirname(parentModulePath), specifier);
74
+ if (!getFromSourceModules(specifier, fsModule, rootDir)) {
75
+ (0, logging_1.log)(`Using external require for ${specifier} from ${parentModulePath}`);
76
+ const resolvedPath = require.resolve(specifier, { paths: [rootDir] });
77
+ (0, logging_1.log)(`Resolved ${specifier} as external package to ${resolvedPath}`);
78
+ // External packages are resolved from the consumer's webpack root.
79
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
80
+ return require(resolvedPath);
81
+ }
82
+ (0, logging_1.log)(`Linking ${parentModulePath} to asset ${specifier}`);
83
+ return evaluateFromFileSystem(absPath, fsModule, rootDir, extraGlobals);
84
+ };
85
+ }
86
+ module.exports = evaluateFromFileSystem;
87
+ //# sourceMappingURL=evaluateFromFileSystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluateFromFileSystem.js","sourceRoot":"","sources":["../src/evaluateFromFileSystem.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { BaseRoute, ExtraGlobals, MapStatsToParams, RenderConcurrency, RouteInput, TransformExpressPath, TransformPath, GetRouteFromRequest } from "./common-types";
2
+ import { Compiler } from "webpack";
3
+ import { Router } from "express";
4
+ interface Options<Route extends BaseRoute = BaseRoute> {
5
+ skipAssets?: boolean;
6
+ getRouteFromRequest?: GetRouteFromRequest<Route>;
7
+ routes?: RouteInput<Route>[];
8
+ mapStatsToParams?: MapStatsToParams;
9
+ renderDirectory?: string;
10
+ renderConcurrency?: RenderConcurrency;
11
+ transformFilePath?: TransformPath<Route>;
12
+ transformExpressPath?: TransformExpressPath<Route>;
13
+ renderEntry?: string;
14
+ extraGlobals?: ExtraGlobals;
15
+ }
16
+ export default class HtmlRenderPlugin<Route extends BaseRoute = BaseRoute> {
17
+ constructor(options?: Options<Route>);
18
+ renderWhenReady: (route: Route) => Promise<string>;
19
+ statsCollectorPlugin: (compiler: Compiler) => void;
20
+ rendererPlugin: (compiler: Compiler) => void;
21
+ render: () => (compiler: Compiler) => void;
22
+ apply: (compiler: Compiler) => void;
23
+ createDevRouter: () => Router;
24
+ }
25
+ export { HtmlRenderPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.HtmlRenderPlugin = void 0;
16
+ const schema_utils_1 = require("schema-utils");
17
+ const HtmlRenderWebpackPlugin_json_1 = __importDefault(require("./schemas/HtmlRenderWebpackPlugin.json"));
18
+ const RenderError_1 = __importDefault(require("./RenderError"));
19
+ const renderRoutes_1 = __importDefault(require("./renderRoutes"));
20
+ const logging_1 = require("./logging");
21
+ const createDevRouter_1 = __importDefault(require("./createDevRouter"));
22
+ const createRenderer_1 = __importDefault(require("./createRenderer"));
23
+ const multiStats_1 = require("./multiStats");
24
+ const timeSince = (startTime) => `${(Date.now() - startTime) / 1000}s`;
25
+ const defaultMapStats = ({ webpackStats }) => webpackStats ? { webpackStats: webpackStats.toJson() } : {};
26
+ const defaultTransform = (route) => route.route;
27
+ class HtmlRenderPlugin {
28
+ constructor(options = {}) {
29
+ (0, schema_utils_1.validate)(HtmlRenderWebpackPlugin_json_1.default, options || {}, {
30
+ name: "HTML Render Webpack Plugin",
31
+ });
32
+ const pluginName = "HtmlRenderPlugin";
33
+ const { extraGlobals = {}, skipAssets = false, mapStatsToParams = defaultMapStats, renderEntry = "main", getRouteFromRequest, transformFilePath = defaultTransform, transformExpressPath = defaultTransform, renderConcurrency = "serial", } = options;
34
+ const routes = (options.routes || [""]).map((route) => typeof route === "string" ? { route } : route);
35
+ const renderDirectory = options.renderDirectory || "dist";
36
+ const clientCompilations = [];
37
+ let rendererCompilation;
38
+ let renderer;
39
+ let lastClientStats = null;
40
+ const isBuildReady = () => rendererCompilation &&
41
+ rendererCompilation.isReady &&
42
+ clientCompilations.every((compilationStatus) => compilationStatus.isReady);
43
+ const isRendererReady = () => isBuildReady() && Boolean(renderer);
44
+ const renderQueue = [];
45
+ const flushRenderQueue = () => __awaiter(this, void 0, void 0, function* () {
46
+ if (isRendererReady() && renderQueue.length > 0) {
47
+ yield renderQueue.shift()();
48
+ flushRenderQueue();
49
+ }
50
+ });
51
+ const render = (route) => __awaiter(this, void 0, void 0, function* () {
52
+ const startRenderTime = Date.now();
53
+ (0, logging_1.log)(`Starting render`, route);
54
+ const webpackStats = getClientStats();
55
+ const renderParams = Object.assign(Object.assign({}, route), mapStatsToParams(Object.assign(Object.assign({}, route), { webpackStats })));
56
+ try {
57
+ const result = yield renderer(renderParams);
58
+ (0, logging_1.log)(`Successfully rendered ${route.route} (${timeSince(startRenderTime)})`);
59
+ return result;
60
+ }
61
+ catch (error) {
62
+ (0, logging_1.log)(`Error rendering ${route.route}`);
63
+ if (error) {
64
+ error.route = route.route;
65
+ error.webpackStats = webpackStats;
66
+ }
67
+ throw error;
68
+ }
69
+ });
70
+ const onRenderAll = (currentCompilation) => __awaiter(this, void 0, void 0, function* () {
71
+ (0, logging_1.log)(`Starting routes render`);
72
+ if (!rendererCompilation.compilation) {
73
+ const errorMessage = `Unable to find render compilation. Something may have gone wrong during render build.`;
74
+ (0, logging_1.logError)(errorMessage);
75
+ throw new Error(errorMessage);
76
+ }
77
+ try {
78
+ yield (0, renderRoutes_1.default)({
79
+ render,
80
+ renderConcurrency,
81
+ renderCompilation: rendererCompilation.compilation,
82
+ renderDirectory,
83
+ renderEntry,
84
+ routes,
85
+ transformFilePath,
86
+ });
87
+ }
88
+ catch (error) {
89
+ (0, logging_1.logError)("An error occurred rendering HTML", error);
90
+ // @ts-expect-error Allow passing errors to compilation
91
+ currentCompilation.errors.push(new RenderError_1.default(error));
92
+ }
93
+ (0, logging_1.log)(`Ending routes render`);
94
+ });
95
+ const getRenderEntry = (compilation) => {
96
+ const renderStats = compilation.getStats().toJson();
97
+ const asset = renderStats.assetsByChunkName[renderEntry];
98
+ if (!asset) {
99
+ throw new Error(`Unable to find renderEntry "${renderEntry}" in assets. Possible entries are: ${Object.keys(renderStats.assetsByChunkName).join(", ")}.`);
100
+ }
101
+ let renderFile = asset;
102
+ if (Array.isArray(renderFile)) {
103
+ renderFile = renderFile[0];
104
+ }
105
+ if (renderFile && typeof renderFile === "object") {
106
+ renderFile = renderFile.name;
107
+ }
108
+ return renderFile;
109
+ };
110
+ const renderCallbacks = [];
111
+ const getClientStats = () => {
112
+ if (lastClientStats) {
113
+ return lastClientStats;
114
+ }
115
+ const clientStats = clientCompilations.length === 1
116
+ ? clientCompilations[0].compilation.getStats()
117
+ : new ((0, multiStats_1.resolveMultiStats)())(clientCompilations
118
+ .map((compilationStatus) => compilationStatus.compilation)
119
+ .filter(Boolean)
120
+ .map((compilation) => compilation.getStats()));
121
+ lastClientStats = clientStats;
122
+ return clientStats;
123
+ };
124
+ const flushQueuedRenders = () => {
125
+ if (isRendererReady() && renderCallbacks.length > 0) {
126
+ renderCallbacks.shift()(renderer, getClientStats());
127
+ flushQueuedRenders();
128
+ }
129
+ };
130
+ const onRendererReady = (cb) => {
131
+ if (isRendererReady()) {
132
+ cb(render);
133
+ }
134
+ else {
135
+ renderCallbacks.push(cb);
136
+ }
137
+ };
138
+ const createRendererIfReady = (currentCompilation) => __awaiter(this, void 0, void 0, function* () {
139
+ if (!isBuildReady()) {
140
+ return;
141
+ }
142
+ const renderCompilation = rendererCompilation.compilation;
143
+ const renderEntry = getRenderEntry(renderCompilation);
144
+ (0, logging_1.log)("Render route:", { renderEntry });
145
+ const rootDir = renderCompilation.compiler.outputPath;
146
+ renderer = (0, createRenderer_1.default)({
147
+ fileSystem: renderCompilation.compiler
148
+ .outputFileSystem,
149
+ fileName: renderEntry,
150
+ rootDir,
151
+ extraGlobals,
152
+ });
153
+ (0, logging_1.log)("Created renderer");
154
+ if (typeof renderer !== "function") {
155
+ (0, logging_1.log)(`Unable to find render function. File "${renderEntry}". Received ${typeof renderer}.`);
156
+ throw new Error(`Unable to find render function. File "${renderEntry}". Received ${typeof renderer}.`);
157
+ }
158
+ flushQueuedRenders();
159
+ flushRenderQueue();
160
+ (0, logging_1.log)("Queued and flushed");
161
+ if (!skipAssets) {
162
+ yield onRenderAll(currentCompilation);
163
+ (0, logging_1.log)("onRenderAll complete");
164
+ }
165
+ });
166
+ const apply = (compiler, isRenderer) => {
167
+ const compilerName = compiler.name || compiler.options.name;
168
+ const compilationStatus = {
169
+ compilation: null,
170
+ isReady: false,
171
+ };
172
+ if (isRenderer) {
173
+ (0, logging_1.log)(`Received render compiler: ${compilerName}`);
174
+ }
175
+ else {
176
+ (0, logging_1.log)(`Received compiler: ${compilerName}`);
177
+ }
178
+ if (isRenderer) {
179
+ if (rendererCompilation) {
180
+ throw new Error("Error. Unable to apply a second renderer");
181
+ }
182
+ rendererCompilation = compilationStatus;
183
+ }
184
+ else {
185
+ clientCompilations.push(compilationStatus);
186
+ }
187
+ compiler.hooks.watchRun.tap(pluginName, () => {
188
+ (0, logging_1.log)(`Build started for for ${compilerName}.`);
189
+ compilationStatus.isReady = false;
190
+ });
191
+ compiler.hooks.afterEmit.tapPromise(pluginName, (compilation) => __awaiter(this, void 0, void 0, function* () {
192
+ (0, logging_1.log)(`Assets emitted for ${compilerName}.`);
193
+ compilationStatus.compilation = compilation;
194
+ lastClientStats = null;
195
+ compilationStatus.isReady = true;
196
+ try {
197
+ yield createRendererIfReady(compilation);
198
+ }
199
+ catch (error) {
200
+ compilation.errors.push(error);
201
+ }
202
+ }));
203
+ };
204
+ this.statsCollectorPlugin = (compiler) => apply(compiler, false);
205
+ this.rendererPlugin = (compiler) => {
206
+ // Support legacy behaviour of calling '.render()' until next breaking change
207
+ if (!compiler) {
208
+ console.warn("Warning. Calling render no longer required. Change htmlRenderPlugin.render() to htmlRenderPlugin.render");
209
+ return (compiler) => apply(compiler, true);
210
+ }
211
+ return apply(compiler, true);
212
+ };
213
+ this.apply = (compiler) => {
214
+ console.warn("Warning. Attempted to apply directly to webpack. Use htmlRenderPlugin.statsCollectorPlugin instead.");
215
+ this.statsCollectorPlugin(compiler);
216
+ };
217
+ this.render = () => this.rendererPlugin;
218
+ this.createDevRouter = () => (0, createDevRouter_1.default)({
219
+ transformExpressPath,
220
+ getRouteFromRequest,
221
+ onRendererReady,
222
+ getClientStats,
223
+ routes,
224
+ });
225
+ this.renderWhenReady = (route) => new Promise((resolve, reject) => {
226
+ const onRender = () => {
227
+ (0, logging_1.log)("Rendering renderWhenReady onRender");
228
+ try {
229
+ resolve(render(route));
230
+ }
231
+ catch (error) {
232
+ reject(error);
233
+ }
234
+ };
235
+ if (isRendererReady()) {
236
+ onRender();
237
+ }
238
+ else {
239
+ renderQueue.push(onRender);
240
+ }
241
+ });
242
+ }
243
+ }
244
+ exports.default = HtmlRenderPlugin;
245
+ exports.HtmlRenderPlugin = HtmlRenderPlugin;
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import debug from "debug";
2
+ export declare const log: debug.Debugger;
3
+ export declare const logError: (...args: any[]) => void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logError = exports.log = void 0;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const node_util_1 = require("node:util");
9
+ exports.log = (0, debug_1.default)("HTMLRenderPlugin");
10
+ const logError = (...args) => {
11
+ console.log((0, node_util_1.styleText)("red", "🚨 HtmlRenderPlugin:"), ...args);
12
+ };
13
+ exports.logError = logError;
14
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,yCAAsC;AAEzB,QAAA,GAAG,GAAG,IAAA,eAAK,EAAC,kBAAkB,CAAC,CAAC;AAEtC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAW,EAAE,EAAE;IACzC,OAAO,CAAC,GAAG,CAAC,IAAA,qBAAS,EAAC,KAAK,EAAE,sBAAsB,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AACjE,CAAC,CAAC;AAFW,QAAA,QAAQ,YAEnB"}
@@ -0,0 +1,6 @@
1
+ import type { Stats } from "webpack";
2
+ import type { WebpackStats } from "./common-types";
3
+ type LoadModule = (id: string) => unknown;
4
+ type MultiStatsConstructor = new (stats: Stats[]) => WebpackStats;
5
+ export declare function resolveMultiStats(loadModule?: LoadModule): MultiStatsConstructor;
6
+ export {};
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveMultiStats = resolveMultiStats;
4
+ // webpack 5.112 moved MultiStats from lib/ to lib/stats/. Both are private
5
+ // paths, so load whichever one exists instead of pinning to a single release.
6
+ const MULTI_STATS_MODULE_PATHS = [
7
+ "webpack/lib/stats/MultiStats", // webpack >= 5.112.0
8
+ "webpack/lib/MultiStats", // webpack <= 5.111.x
9
+ ];
10
+ function getWebpackVersion(loadModule) {
11
+ try {
12
+ return loadModule("webpack/package.json").version;
13
+ }
14
+ catch (_a) {
15
+ return "unknown";
16
+ }
17
+ }
18
+ function resolveMultiStats(loadModule = require) {
19
+ var _a;
20
+ const failures = [];
21
+ for (const modulePath of MULTI_STATS_MODULE_PATHS) {
22
+ try {
23
+ const loaded = loadModule(modulePath);
24
+ return ((_a = loaded.default) !== null && _a !== void 0 ? _a : loaded);
25
+ }
26
+ catch (error) {
27
+ failures.push(`${modulePath}: ${error instanceof Error ? error.message : error}`);
28
+ }
29
+ }
30
+ throw new Error(`Unable to load webpack's MultiStats class. Installed webpack: ${getWebpackVersion(loadModule)}. Tried ${failures.join(", ")}.`);
31
+ }
32
+ //# sourceMappingURL=multiStats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multiStats.js","sourceRoot":"","sources":["../src/multiStats.ts"],"names":[],"mappings":";;AAqBA,8CA0BC;AAzCD,2EAA2E;AAC3E,8EAA8E;AAC9E,MAAM,wBAAwB,GAAG;IAC/B,8BAA8B,EAAE,qBAAqB;IACrD,wBAAwB,EAAE,qBAAqB;CAChD,CAAC;AAEF,SAAS,iBAAiB,CAAC,UAAsB;IAC/C,IAAI,CAAC;QACH,OAAQ,UAAU,CAAC,sBAAsB,CAAyB,CAAC,OAAO,CAAC;IAC7E,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAgB,iBAAiB,CAC/B,aAAyB,OAAO;;IAEhC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,UAAU,IAAI,wBAAwB,EAAE,CAAC;QAClD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CACwB,CAAC;YAE7D,OAAO,CACL,MAAC,MAA8C,CAAC,OAAO,mCACtD,MAAgC,CAClC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CACX,GAAG,UAAU,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,iEAAiE,iBAAiB,CAChF,UAAU,CACX,WAAW,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { RenderConcurrency, TransformPath, Render, BaseRoute } from "./common-types";
2
+ import { Compilation } from "webpack";
3
+ interface Params<Route> {
4
+ render: Render<Route>;
5
+ renderConcurrency: RenderConcurrency;
6
+ routes: Route[];
7
+ renderEntry: string;
8
+ renderDirectory: string;
9
+ renderCompilation: Compilation;
10
+ transformFilePath: TransformPath<Route>;
11
+ }
12
+ export default function renderRoutes<Route extends BaseRoute>({ render: performRender, renderConcurrency, routes, renderDirectory, renderCompilation, transformFilePath, }: Params<Route>): Promise<void[] | undefined>;
13
+ export {};
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.default = renderRoutes;
16
+ const path_1 = __importDefault(require("path"));
17
+ const node_util_1 = require("node:util");
18
+ const logging_1 = require("./logging");
19
+ function safeMkdir(fileSystem, dir) {
20
+ // @ts-expect-error Allow looking for invalid field mkdirp
21
+ if (fileSystem.mkdirp) {
22
+ (0, logging_1.log)("Found mkdirp method on fileSystem. Assuming older mock fs system.");
23
+ return new Promise((resolve, reject) => {
24
+ // @ts-expect-error Allow calling for invalid field mkdirp
25
+ return fileSystem.mkdirp(dir, (error) => {
26
+ if (error) {
27
+ reject(error);
28
+ return;
29
+ }
30
+ resolve();
31
+ });
32
+ });
33
+ }
34
+ return new Promise((resolve, reject) => {
35
+ fileSystem.mkdir(dir, { recursive: true }, (error) => {
36
+ if (error) {
37
+ // @ts-expect-error Looking for code property that shouldn't exist
38
+ if (error.code === "EEXIST") {
39
+ (0, logging_1.log)("Ignoring error when creating folder. Folder already existed. Assuming older mock fs systems.");
40
+ resolve();
41
+ return;
42
+ }
43
+ reject(error);
44
+ return;
45
+ }
46
+ resolve();
47
+ });
48
+ });
49
+ }
50
+ function renderRoutes(_a) {
51
+ return __awaiter(this, arguments, void 0, function* ({ render: performRender, renderConcurrency, routes, renderDirectory, renderCompilation, transformFilePath, }) {
52
+ (0, logging_1.log)(`Starting render of ${routes.length} routes`);
53
+ const outputFileSystem = renderCompilation.compiler.outputFileSystem;
54
+ if (!outputFileSystem) {
55
+ throw new Error("Unable to render HTML: the compilation has no output file system.");
56
+ }
57
+ const fileSystem = outputFileSystem;
58
+ function emitFile(dir, content) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ (0, logging_1.log)("Emitting file to", dir);
61
+ yield safeMkdir(fileSystem, path_1.default.dirname(dir));
62
+ return new Promise((resolve, reject) => fileSystem.writeFile(dir, content, (error) => {
63
+ if (error) {
64
+ reject(error);
65
+ return;
66
+ }
67
+ resolve();
68
+ }));
69
+ });
70
+ }
71
+ function render(route) {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ const relativeFilePath = transformFilePath(route);
74
+ const includesHtmlInFilePath = relativeFilePath.substr(-5) === ".html";
75
+ if (!path_1.default.isAbsolute(renderDirectory)) {
76
+ renderDirectory = path_1.default.resolve(renderDirectory);
77
+ }
78
+ const newFilePath = includesHtmlInFilePath
79
+ ? path_1.default.join(renderDirectory, relativeFilePath)
80
+ : path_1.default.join(renderDirectory, relativeFilePath, "index.html");
81
+ let renderResult;
82
+ try {
83
+ renderResult = yield performRender(route);
84
+ }
85
+ catch (error) {
86
+ console.error(`🚨 ${(0, node_util_1.styleText)("red", `An error occurred rendering route: "${route.route}"`)}. Exiting render.`);
87
+ throw error;
88
+ }
89
+ if (typeof renderResult !== "string") {
90
+ throw new Error(`Render must return a string. Received "${typeof renderResult}".`);
91
+ }
92
+ yield emitFile(newFilePath, renderResult);
93
+ (0, logging_1.log)(`Successfully emitted ${newFilePath}`);
94
+ });
95
+ }
96
+ if (renderConcurrency === "parallel") {
97
+ (0, logging_1.log)("Rendering parallel");
98
+ return Promise.all(routes.map(render));
99
+ }
100
+ (0, logging_1.log)("Rendering Serial");
101
+ for (const route of routes) {
102
+ yield render(route);
103
+ }
104
+ return;
105
+ });
106
+ }
107
+ //# sourceMappingURL=renderRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderRoutes.js","sourceRoot":"","sources":["../src/renderRoutes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ {
2
+ "title": "HTMLRenderOptionsArgument",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "routes": {
7
+ "description": "List of routes to render. A file is created for each route.",
8
+ "type": "array",
9
+ "items": {
10
+ "description": "A rule condition",
11
+ "anyOf": [
12
+ {
13
+ "type": "object",
14
+ "additionalProperties": true,
15
+ "properties": {
16
+ "route": {
17
+ "description": "The path to generate",
18
+ "type": "string"
19
+ }
20
+ }
21
+ },
22
+ { "type": "string" }
23
+ ]
24
+ }
25
+ },
26
+ "skipAssets": {
27
+ "description": "If true, skip creating assets after a build. Often used in conjuction with createDevRouter.",
28
+ "type": "boolean"
29
+ },
30
+ "mapStatsToParams": {
31
+ "description": "Used to pass additional values to each render.",
32
+ "instanceof": "Function"
33
+ },
34
+ "renderDirectory": {
35
+ "description": "The directory to render files to.",
36
+ "type": "string"
37
+ },
38
+ "renderConcurrency": {
39
+ "description": "Whether to render serial or parallel",
40
+ "type": "string"
41
+ },
42
+ "transformFilePath": {
43
+ "description": "Used to build the file path from the route.",
44
+ "instanceof": "Function"
45
+ },
46
+ "transformExpressPath": {
47
+ "description": "Used to build the express route for the dev server.",
48
+ "instanceof": "Function"
49
+ },
50
+ "getRouteFromRequest": {
51
+ "description": "Used on the dev server to identify a route for a request.",
52
+ "instanceof": "Function"
53
+ },
54
+ "renderEntry": {
55
+ "description": "Which webpack entry should be used to render.",
56
+ "type": "string"
57
+ },
58
+ "extraGlobals": {
59
+ "description": "Extra values to pass to the evaluated renderer's module scope.",
60
+ "type": "object"
61
+ }
62
+ }
63
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "html-render-webpack-plugin",
3
+ "description": "webpack plugin for rendering static HTML in a multi-config webpack build",
4
+ "author": "Jahred Hope <jahredhope@gmail.com>",
5
+ "version": "0.0.0-master-20261021073115",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "html",
14
+ "static",
15
+ "plugin",
16
+ "webpack"
17
+ ],
18
+ "engines": {
19
+ "node": ">=22.13.0"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/jahredhope/html-render-webpack-plugin.git"
24
+ },
25
+ "peerDependencies": {
26
+ "express": ">=4.0.0",
27
+ "webpack": "^5.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@changesets/cli": "^3.0.3",
31
+ "@eslint/js": "^10.0.1",
32
+ "@types/debug": "^4.1.13",
33
+ "@types/express": "^5.0.6",
34
+ "@types/node": "^22.10.0",
35
+ "commitizen": "4.2.3",
36
+ "cz-conventional-changelog": "3.3.0",
37
+ "eslint": "10.13.0",
38
+ "express": "^5.3.0",
39
+ "globals": "^17.13.0",
40
+ "memfs": "^4.80.0",
41
+ "prettier": "3.9.10",
42
+ "react": "^17.0.1",
43
+ "react-dom": "^17.0.1",
44
+ "typescript": "^5.9.3",
45
+ "typescript-eslint": "^8.72.0",
46
+ "vitest": "^5.0.5",
47
+ "webpack": "^5.112.0",
48
+ "webpack-merge": "^6.0.1"
49
+ },
50
+ "dependencies": {
51
+ "debug": "^4.4.3",
52
+ "exception-formatter": "^2.1.2",
53
+ "schema-utils": "^4.8.1"
54
+ },
55
+ "config": {
56
+ "commitizen": {
57
+ "path": "cz-conventional-changelog"
58
+ }
59
+ },
60
+ "scripts": {
61
+ "build": "tsc --declaration",
62
+ "changeset": "changeset",
63
+ "commit": "git-cz",
64
+ "format": "prettier --write .",
65
+ "lint": "pnpm run lint.eslint && pnpm run lint.prettier && pnpm run lint.typecheck",
66
+ "lint.eslint": "eslint .",
67
+ "lint.prettier": "prettier --check .",
68
+ "lint.typecheck": "tsc -p tsconfig.test.json",
69
+ "release": "changeset publish",
70
+ "test": "vitest run",
71
+ "test:watch": "vitest",
72
+ "version-packages": "changeset version"
73
+ }
74
+ }