1 | "use strict";
|
2 |
|
3 | const fs = require("fs-extra");
|
4 | const path = require("path");
|
5 | const nunjucks = require("nunjucks");
|
6 |
|
7 | const pluginName = "NunjucksWebpackPlugin";
|
8 |
|
9 | class NunjucksWebpackPlugin {
|
10 | constructor(options) {
|
11 | this.options = Object.assign(
|
12 | {},
|
13 | {
|
14 | configure: {
|
15 | options: {},
|
16 | path: ""
|
17 | },
|
18 | templates: []
|
19 | },
|
20 | options || {}
|
21 | );
|
22 |
|
23 | if (
|
24 | !Array.isArray(this.options.templates) ||
|
25 | this.options.templates.length === 0
|
26 | ) {
|
27 | throw new Error("Options `templates` must be an empty array");
|
28 | }
|
29 | }
|
30 |
|
31 | apply(compiler) {
|
32 | const fileDependencies = [];
|
33 |
|
34 | let output = compiler.options.output.path;
|
35 |
|
36 | if (
|
37 | output === "/" &&
|
38 | compiler.options.devServer &&
|
39 | compiler.options.devServer.outputPath
|
40 | ) {
|
41 | output = compiler.options.devServer.outputPath;
|
42 | }
|
43 |
|
44 | const emitCallback = (compilation, callback) => {
|
45 | const configure =
|
46 | this.options.configure instanceof nunjucks.Environment
|
47 | ? this.options.configure
|
48 | : nunjucks.configure(
|
49 | this.options.configure.path,
|
50 | this.options.configure.options
|
51 | );
|
52 |
|
53 | const promises = [];
|
54 |
|
55 | this.options.templates.forEach(template => {
|
56 | if (!template.from) {
|
57 | throw new Error("Each template should have `from` option");
|
58 | }
|
59 |
|
60 | if (!template.to) {
|
61 | throw new Error("Each template should have `to` option");
|
62 | }
|
63 |
|
64 | if (fileDependencies.indexOf(template.from) === -1) {
|
65 | fileDependencies.push(template.from);
|
66 | }
|
67 |
|
68 | const res = configure.render(
|
69 | template.from,
|
70 | template.context ? template.context : null,
|
71 | template.callback ? template.callback : null
|
72 | );
|
73 |
|
74 | let webpackTo = template.to;
|
75 |
|
76 | if (path.isAbsolute(webpackTo)) {
|
77 | webpackTo = path.relative(output, webpackTo);
|
78 | }
|
79 |
|
80 | const source = {
|
81 | size: () => res.length,
|
82 | source: () => res
|
83 | };
|
84 |
|
85 | compilation.assets[webpackTo] = source;
|
86 |
|
87 | if (template.writeToFileEmit) {
|
88 | const fileDest = path.join(output, webpackTo);
|
89 |
|
90 | promises.push(fs.outputFile(fileDest, source.source()));
|
91 | }
|
92 | });
|
93 |
|
94 | return (
|
95 | Promise.all(promises)
|
96 |
|
97 | .then(() => callback())
|
98 | .catch(error => {
|
99 | compilation.errors.push(error);
|
100 |
|
101 |
|
102 | return callback();
|
103 | })
|
104 | );
|
105 | };
|
106 |
|
107 | const afterEmitCallback = (compilation, callback) => {
|
108 | let compilationFileDependencies = compilation.fileDependencies;
|
109 | let addFileDependency = file => compilation.fileDependencies.add(file);
|
110 |
|
111 | if (Array.isArray(compilation.fileDependencies)) {
|
112 | compilationFileDependencies = new Set(compilation.fileDependencies);
|
113 | addFileDependency = file => compilation.fileDependencies.push(file);
|
114 | }
|
115 |
|
116 | for (const file of fileDependencies) {
|
117 | if (!compilationFileDependencies.has(file)) {
|
118 | addFileDependency(file);
|
119 | }
|
120 | }
|
121 |
|
122 | return callback();
|
123 | };
|
124 |
|
125 | if (compiler.hooks) {
|
126 | compiler.hooks.emit.tapAsync(pluginName, emitCallback);
|
127 | compiler.hooks.afterEmit.tapAsync(pluginName, afterEmitCallback);
|
128 | } else {
|
129 | compiler.plugin("emit", emitCallback);
|
130 | compiler.plugin("after-emit", afterEmitCallback);
|
131 | }
|
132 | }
|
133 | }
|
134 |
|
135 | module.exports = NunjucksWebpackPlugin;
|