cyclops-infobook-html 4.0.1 → 5.1.0
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/README.md +23 -2
- package/bin/compress-icons.d.ts +2 -0
- package/bin/compress-icons.js +47 -0
- package/bin/generate-icons.d.ts +2 -0
- package/bin/generate-icons.js +68 -0
- package/bin/generate-infobook-html.js +16 -8
- package/bin/generate-mod-metadata.js +15 -7
- package/index.d.ts +2 -0
- package/index.js +39 -24
- package/lib/icon/IconsCompressor.d.ts +18 -0
- package/lib/icon/IconsCompressor.js +81 -0
- package/lib/icon/IconsGenerator.d.ts +128 -0
- package/lib/icon/IconsGenerator.js +597 -0
- package/lib/infobook/FileWriter.d.ts +1 -2
- package/lib/infobook/FileWriter.js +21 -6
- package/lib/infobook/IFileWriter.d.ts +1 -2
- package/lib/infobook/IInfoAppendix.d.ts +1 -1
- package/lib/infobook/InfoBookInitializer.js +10 -2
- package/lib/infobook/appendix/InfoBookAppendixAd.d.ts +1 -1
- package/lib/infobook/appendix/InfoBookAppendixAd.js +13 -2
- package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.d.ts +1 -1
- package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.js +13 -4
- package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.js +14 -5
- package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.d.ts +1 -1
- package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.js +39 -28
- package/lib/infobook/appendix/InfoBookAppendixHandlerImage.js +13 -4
- package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.js +12 -3
- package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.d.ts +1 -1
- package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.js +16 -5
- package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.js +11 -2
- package/lib/infobook/appendix/InfoBookAppendixTagIndex.d.ts +1 -1
- package/lib/infobook/appendix/InfoBookAppendixTagIndex.js +31 -20
- package/lib/modloader/ModLoader.d.ts +2 -3
- package/lib/modloader/ModLoader.js +56 -48
- package/lib/parse/XmlInfoBookParser.js +2 -2
- package/lib/resource/ResourceHandler.d.ts +2 -1
- package/lib/resource/ResourceHandler.js +7 -3
- package/lib/resource/ResourceLoader.d.ts +5 -5
- package/lib/resource/ResourceLoader.js +37 -28
- package/lib/serialize/HtmlInfoBookSerializer.d.ts +2 -2
- package/lib/serialize/HtmlInfoBookSerializer.js +80 -65
- package/package.json +10 -6
|
@@ -1,7 +1,15 @@
|
|
|
1
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
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.HtmlInfoBookSerializer = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
13
|
const fs_1 = require("fs");
|
|
6
14
|
const mkdirp = require("mkdirp");
|
|
7
15
|
const ncp_1 = require("ncp");
|
|
@@ -15,20 +23,20 @@ const FileWriter_1 = require("../infobook/FileWriter");
|
|
|
15
23
|
*/
|
|
16
24
|
class HtmlInfoBookSerializer {
|
|
17
25
|
constructor() {
|
|
18
|
-
this.templateIndex = pug_1.compileFile(__dirname + '/../../template/index.pug');
|
|
19
|
-
this.templateSection = pug_1.compileFile(__dirname + '/../../template/section.pug');
|
|
20
|
-
this.appendixWrapper = pug_1.compileFile(__dirname + '/../../template/appendix/appendix_base.pug');
|
|
21
|
-
this.templateItem = pug_1.compileFile(__dirname + '/../../template/appendix/item.pug');
|
|
26
|
+
this.templateIndex = (0, pug_1.compileFile)(__dirname + '/../../template/index.pug');
|
|
27
|
+
this.templateSection = (0, pug_1.compileFile)(__dirname + '/../../template/section.pug');
|
|
28
|
+
this.appendixWrapper = (0, pug_1.compileFile)(__dirname + '/../../template/appendix/appendix_base.pug');
|
|
29
|
+
this.templateItem = (0, pug_1.compileFile)(__dirname + '/../../template/appendix/item.pug');
|
|
22
30
|
}
|
|
23
31
|
serialize(infobook, context, assetsPaths) {
|
|
24
|
-
return
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
33
|
context = Object.assign(Object.assign({}, context), { basePath: context.path, breadcrumbs: [] });
|
|
26
34
|
this.fileWriter = new FileWriter_1.FileWriter(context);
|
|
27
35
|
yield this.ensureDirExists(context.path);
|
|
28
|
-
yield this.ensureDirExists(path_1.join(context.path, 'assets'));
|
|
29
|
-
yield this.ensureDirExists(path_1.join(context.path, 'assets', '
|
|
36
|
+
yield this.ensureDirExists((0, path_1.join)(context.path, 'assets'));
|
|
37
|
+
yield this.ensureDirExists((0, path_1.join)(context.path, 'assets', 'icon'));
|
|
30
38
|
// Create a .nojekyll file to ensure _lang directories are served via GitHub pages.
|
|
31
|
-
yield fs_1.promises.writeFile(path_1.join(context.path, '.nojekyll'), '');
|
|
39
|
+
yield fs_1.promises.writeFile((0, path_1.join)(context.path, '.nojekyll'), '');
|
|
32
40
|
// Serialize sections in all languages
|
|
33
41
|
for (const language of context.resourceHandler.getLanguages()) {
|
|
34
42
|
const langPath = this.getLanguagePath(language, context.path);
|
|
@@ -37,21 +45,21 @@ class HtmlInfoBookSerializer {
|
|
|
37
45
|
yield this.serializeSectionFiles(infobook, context, language, langPath, sectionIndex);
|
|
38
46
|
}
|
|
39
47
|
// Serialize assets
|
|
40
|
-
yield util_1.promisify(ncp_1.ncp)(__dirname + '/../../assets/', path_1.join(context.path, 'assets'));
|
|
48
|
+
yield (0, util_1.promisify)(ncp_1.ncp)(__dirname + '/../../assets/', (0, path_1.join)(context.path, 'assets'));
|
|
41
49
|
for (const assetsPath of assetsPaths) {
|
|
42
|
-
yield util_1.promisify(ncp_1.ncp)(assetsPath, path_1.join(context.path, 'assets'));
|
|
50
|
+
yield (0, util_1.promisify)(ncp_1.ncp)(assetsPath, (0, path_1.join)(context.path, 'assets'));
|
|
43
51
|
}
|
|
44
52
|
});
|
|
45
53
|
}
|
|
46
54
|
serializeSectionIndex(infobook, contextRoot, language, langPath) {
|
|
47
|
-
return
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
56
|
const sectionIndex = {
|
|
49
57
|
linkedPagesList: [],
|
|
50
58
|
tags: {},
|
|
51
59
|
urlIndex: {},
|
|
52
60
|
};
|
|
53
61
|
let pageIndex = 0;
|
|
54
|
-
yield this.serializeSection(infobook.rootSection, Object.assign(Object.assign({}, contextRoot), { language, path: langPath }), (
|
|
62
|
+
yield this.serializeSection(infobook.rootSection, Object.assign(Object.assign({}, contextRoot), { language, path: langPath }), (_a) => __awaiter(this, [_a], void 0, function* ({ index, section, sectionTitle, fileUrl, breadcrumbs }) {
|
|
55
63
|
if (!index) {
|
|
56
64
|
sectionIndex.urlIndex[fileUrl] = pageIndex++;
|
|
57
65
|
const name = breadcrumbs.slice(1).map((b) => b.name).join(' / ');
|
|
@@ -68,13 +76,13 @@ class HtmlInfoBookSerializer {
|
|
|
68
76
|
});
|
|
69
77
|
}
|
|
70
78
|
serializeSectionFiles(infobook, contextRoot, language, langPath, sectionIndex) {
|
|
71
|
-
return
|
|
72
|
-
yield this.serializeSection(infobook.rootSection, Object.assign(Object.assign({}, contextRoot), { language, path: langPath, sectionIndex }), ({ index, breadcrumbs, context, section, sectionTitle, subSectionDatas, filePath, fileUrl })
|
|
79
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
yield this.serializeSection(infobook.rootSection, Object.assign(Object.assign({}, contextRoot), { language, path: langPath, sectionIndex }), (_a) => __awaiter(this, [_a], void 0, function* ({ index, breadcrumbs, context, section, sectionTitle, subSectionDatas, filePath, fileUrl }) {
|
|
73
81
|
// Create links to this page in other languages
|
|
74
82
|
const languages = [];
|
|
75
83
|
for (const name of contextRoot.resourceHandler.getLanguages()) {
|
|
76
|
-
const baseFilePath = filePath.substr(path_1.join(contextRoot.basePath, this.getLanguagePath(language)).length);
|
|
77
|
-
const languageFilePath = path_1.join(contextRoot.basePath, this.getLanguagePath(name), baseFilePath);
|
|
84
|
+
const baseFilePath = filePath.substr((0, path_1.join)(contextRoot.basePath, this.getLanguagePath(language)).length);
|
|
85
|
+
const languageFilePath = (0, path_1.join)(contextRoot.basePath, this.getLanguagePath(name), baseFilePath);
|
|
78
86
|
const url = this.filePathToUrl(languageFilePath, contextRoot.basePath, context.baseUrl);
|
|
79
87
|
languages.push({ name, url });
|
|
80
88
|
}
|
|
@@ -97,23 +105,26 @@ class HtmlInfoBookSerializer {
|
|
|
97
105
|
if (context.googleAdsense && language === 'en_us') {
|
|
98
106
|
appendices.unshift(new InfoBookAppendixAd_1.InfoBookAppendixAd());
|
|
99
107
|
}
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.filter((appendix) => appendix) // TODO: rm
|
|
105
|
-
.map((appendix) => {
|
|
106
|
-
const appendixContents = appendix.toHtml(context, this.fileWriter, this);
|
|
108
|
+
const sectionAppendices = [];
|
|
109
|
+
for (const appendix of appendices) {
|
|
110
|
+
if (appendix) {
|
|
111
|
+
const appendixContents = yield appendix.toHtml(context, this.fileWriter, this);
|
|
107
112
|
if (appendix.skipWrapper) {
|
|
108
|
-
|
|
113
|
+
sectionAppendices.push(appendixContents);
|
|
109
114
|
}
|
|
110
115
|
else {
|
|
111
|
-
|
|
116
|
+
sectionAppendices.push(this.appendixWrapper({
|
|
112
117
|
appendixContents,
|
|
113
118
|
appendixName: appendix.getName ? appendix.getName(context) : null,
|
|
114
|
-
});
|
|
119
|
+
}));
|
|
115
120
|
}
|
|
116
|
-
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Create leaf file
|
|
124
|
+
const fileContents = this.templateSection(Object.assign(Object.assign({}, context), { breadcrumbs, headSuffix: context.headSuffixGetters.map((g) => g(context)).join(''), languages,
|
|
125
|
+
nextPage,
|
|
126
|
+
previousPage,
|
|
127
|
+
sectionAppendices, sectionParagraphs: section.paragraphTranslationKeys
|
|
117
128
|
.map((key) => context.resourceHandler.getTranslation(key, context.language))
|
|
118
129
|
.map((value) => this.formatString(value)), sectionTitle }));
|
|
119
130
|
yield fs_1.promises.writeFile(filePath, fileContents);
|
|
@@ -122,14 +133,14 @@ class HtmlInfoBookSerializer {
|
|
|
122
133
|
});
|
|
123
134
|
}
|
|
124
135
|
serializeSection(section, context, onSection) {
|
|
125
|
-
return
|
|
136
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
137
|
const sectionTitle = this.formatString(context.resourceHandler
|
|
127
138
|
.getTranslation(section.nameTranslationKey, context.language));
|
|
128
139
|
const breadcrumbs = context.breadcrumbs.concat([{ name: sectionTitle }]);
|
|
129
140
|
// Go in a subfolder when we are handling a different mod
|
|
130
141
|
if (section.modId !== context.modId) {
|
|
131
|
-
yield this.ensureDirExists(path_1.join(context.path, section.modId));
|
|
132
|
-
context = Object.assign(Object.assign({}, context), { modId: section.modId, path: path_1.join(context.path, section.modId) });
|
|
142
|
+
yield this.ensureDirExists((0, path_1.join)(context.path, section.modId));
|
|
143
|
+
context = Object.assign(Object.assign({}, context), { modId: section.modId, path: (0, path_1.join)(context.path, section.modId) });
|
|
133
144
|
}
|
|
134
145
|
if (section.subSections && section.subSections.length > 0) {
|
|
135
146
|
// Navigation section
|
|
@@ -141,11 +152,11 @@ class HtmlInfoBookSerializer {
|
|
|
141
152
|
url: fileUrl,
|
|
142
153
|
}]);
|
|
143
154
|
for (const subSection of section.subSections) {
|
|
144
|
-
const subSectionData = yield this.serializeSection(subSection, Object.assign(Object.assign({}, context), { breadcrumbs: subBreadcrumbs, path: path_1.join(context.path, subSection.nameTranslationKey
|
|
155
|
+
const subSectionData = yield this.serializeSection(subSection, Object.assign(Object.assign({}, context), { breadcrumbs: subBreadcrumbs, path: (0, path_1.join)(context.path, subSection.nameTranslationKey
|
|
145
156
|
.substr(subSection.nameTranslationKey.lastIndexOf('.') + 1)), root: false }), onSection);
|
|
146
157
|
subSectionDatas.push(Object.assign(Object.assign({}, subSectionData), { url: this.filePathToUrl(subSectionData.filePath, context.basePath, context.baseUrl) }));
|
|
147
158
|
}
|
|
148
|
-
const filePath = path_1.join(context.path, 'index.html');
|
|
159
|
+
const filePath = (0, path_1.join)(context.path, 'index.html');
|
|
149
160
|
yield onSection({ index: true, breadcrumbs, context, sectionTitle, section, subSectionDatas, filePath, fileUrl });
|
|
150
161
|
return { filePath: context.path, sectionTitle };
|
|
151
162
|
}
|
|
@@ -173,43 +184,47 @@ class HtmlInfoBookSerializer {
|
|
|
173
184
|
}
|
|
174
185
|
return { link, linkTarget };
|
|
175
186
|
}
|
|
176
|
-
createItemDisplay(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
createItemDisplay(resourceHandler_1, context_1, fileWriter_1, item_1, slot_1) {
|
|
188
|
+
return __awaiter(this, arguments, void 0, function* (resourceHandler, context, fileWriter, item, slot, annotation = '') {
|
|
189
|
+
if (item.item === 'minecraft:air') {
|
|
190
|
+
return slot ? '<div class="item item-slot"> </div>' : '<div class="item"> </div>';
|
|
191
|
+
}
|
|
192
|
+
const icon = resourceHandler.getItemIconFile(item.item, item.components);
|
|
193
|
+
if (!icon) {
|
|
194
|
+
throw new Error(`Could not find an icon for item ${JSON.stringify(item)}`);
|
|
195
|
+
}
|
|
196
|
+
const iconUrl = yield fileWriter.write('icon/' + (0, path_1.basename)(icon), () => (0, fs_1.createReadStream)(icon));
|
|
197
|
+
const key = resourceHandler.getItemTranslationKey(item);
|
|
198
|
+
if (!key) {
|
|
199
|
+
throw new Error(`Could not find translation key for item ${JSON.stringify(item)}`);
|
|
200
|
+
}
|
|
201
|
+
const { link, linkTarget } = this.createResourceLink(resourceHandler, context, item.item, key);
|
|
202
|
+
return this.templateItem(Object.assign(Object.assign({}, context), { annotation, count: item.count || 1, icon: iconUrl, link,
|
|
203
|
+
linkTarget, name: resourceHandler.getTranslation(resourceHandler.getItemTranslationKey(item), context.language), slot }));
|
|
204
|
+
});
|
|
192
205
|
}
|
|
193
206
|
createFluidDisplay(resourceHandler, context, fileWriter, fluid, slot) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
207
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
208
|
+
const icon = resourceHandler.getFluidIconFile(fluid.fluid);
|
|
209
|
+
if (!icon) {
|
|
210
|
+
throw new Error(`Could not find an icon for fluid ${JSON.stringify(fluid)}`);
|
|
211
|
+
}
|
|
212
|
+
const iconUrl = yield fileWriter.write('icon/' + (0, path_1.basename)(icon), () => (0, fs_1.createReadStream)(icon));
|
|
213
|
+
const key = resourceHandler.getFluidTranslationKey(fluid);
|
|
214
|
+
if (!key) {
|
|
215
|
+
throw new Error(`Could not find translation key for fluid ${JSON.stringify(fluid)}`);
|
|
216
|
+
}
|
|
217
|
+
const { link, linkTarget } = this.createResourceLink(resourceHandler, context, this.tagFluid(context, fluid.fluid), key);
|
|
218
|
+
return this.templateItem(Object.assign(Object.assign({}, context), { count: (fluid.amount || 1), icon: iconUrl, link,
|
|
219
|
+
linkTarget, name: resourceHandler.getTranslation(resourceHandler.getFluidTranslationKey(fluid), context.language), slot }));
|
|
220
|
+
});
|
|
206
221
|
}
|
|
207
222
|
tagFluid(context, fluidName) {
|
|
208
223
|
return fluidName;
|
|
209
224
|
}
|
|
210
225
|
getLanguagePath(language, path) {
|
|
211
226
|
path = path || '';
|
|
212
|
-
return language === 'en_us' ? path : path_1.join(path, '_lang', language);
|
|
227
|
+
return language === 'en_us' ? path : (0, path_1.join)(path, '_lang', language);
|
|
213
228
|
}
|
|
214
229
|
/**
|
|
215
230
|
* Convert Minecraft formatting codes to HTML formats.
|
|
@@ -248,13 +263,13 @@ class HtmlInfoBookSerializer {
|
|
|
248
263
|
return value;
|
|
249
264
|
}
|
|
250
265
|
ensureDirExists(dirPath) {
|
|
251
|
-
return
|
|
266
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
252
267
|
let fstat;
|
|
253
268
|
try {
|
|
254
269
|
fstat = yield fs_1.promises.stat(dirPath);
|
|
255
270
|
}
|
|
256
271
|
catch (e) {
|
|
257
|
-
yield util_1.promisify(mkdirp)(dirPath);
|
|
272
|
+
yield (0, util_1.promisify)(mkdirp)(dirPath);
|
|
258
273
|
}
|
|
259
274
|
if (fstat && !fstat.isDirectory() && fstat.isFile()) {
|
|
260
275
|
throw new Error(`Could not serialize to a file, must be a directory.`);
|
|
@@ -263,7 +278,7 @@ class HtmlInfoBookSerializer {
|
|
|
263
278
|
}
|
|
264
279
|
filePathToUrl(filePath, basePath, baseUrl) {
|
|
265
280
|
let url = filePath.replace(basePath, baseUrl);
|
|
266
|
-
const last = path_1.basename(url);
|
|
281
|
+
const last = (0, path_1.basename)(url);
|
|
267
282
|
if (!url.endsWith('/') && last.indexOf('.') < 0) {
|
|
268
283
|
url = url + '/';
|
|
269
284
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyclops-infobook-html",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Output Cyclops infobooks as HTML",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "git@github.com:CyclopsMC/infobook-html.git",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"bin": {
|
|
14
14
|
"generate-cyclops-infobook-html": "bin/generate-infobook-html.js",
|
|
15
|
-
"generate-mod-metadata": "bin/generate-mod-metadata.js"
|
|
15
|
+
"generate-mod-metadata": "bin/generate-mod-metadata.js",
|
|
16
|
+
"generate-icons": "bin/generate-icons.js",
|
|
17
|
+
"compress-icons": "bin/compress-icons.js"
|
|
16
18
|
},
|
|
17
19
|
"files": [
|
|
18
20
|
"bin/**/*.d.ts",
|
|
@@ -50,16 +52,16 @@
|
|
|
50
52
|
"testEnvironment": "node"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
|
-
"@types/jest": "^
|
|
55
|
+
"@types/jest": "^30.0.0",
|
|
54
56
|
"@types/minimist": "^1.2.0",
|
|
55
57
|
"coveralls": "^3.0.3",
|
|
56
|
-
"jest": "^
|
|
58
|
+
"jest": "^30.0.0",
|
|
57
59
|
"manual-git-changelog": "^1.0.1",
|
|
58
60
|
"pre-commit": "^1.2.2",
|
|
59
|
-
"ts-jest": "^
|
|
61
|
+
"ts-jest": "^29.0.0",
|
|
60
62
|
"tslint": "^6.0.0",
|
|
61
63
|
"tslint-eslint-rules": "^5.4.0",
|
|
62
|
-
"typescript": "^
|
|
64
|
+
"typescript": "^5.0.0"
|
|
63
65
|
},
|
|
64
66
|
"scripts": {
|
|
65
67
|
"test": "jest ${1}",
|
|
@@ -81,9 +83,11 @@
|
|
|
81
83
|
"@types/rimraf": "^3.0.0",
|
|
82
84
|
"@types/xml2js": "^0.4.4",
|
|
83
85
|
"@types/yauzl": "^2.9.1",
|
|
86
|
+
"@types/minimist": "^1.2.0",
|
|
84
87
|
"minimist": "^1.2.0",
|
|
85
88
|
"mkdirp": "^0.5.1",
|
|
86
89
|
"mvn-artifact-download": "^5.0.0",
|
|
90
|
+
"optipng-bin": "^9.0.0",
|
|
87
91
|
"ncp": "^2.0.0",
|
|
88
92
|
"node-fetch": "^2.6.0",
|
|
89
93
|
"pug": "^3.0.0",
|