Merge pull request #1176 from clusterio/fix/export-skip-broken-icons

Skip icons that fail to export instead of failing the export
This commit is contained in:
Daniel authored and GitHub committed 2026-11-01 18:24:58 +01:00
commit 88855251e9
2 files changed
+53 -2

No files matched your search

+10 -1
View File
@@ -413,7 +413,13 @@ async function exportIcons(
state.metadata[baseName] ??= metadata;
let count = 0;
for (let prototype of prototypes) {
const icon = await exportIcon(server, modVersions, zipCache, state, prototype);
let icon: lib.ExportMetadataEntry["icon"];
try {
icon = await exportIcon(server, modVersions, zipCache, state, prototype);
} catch (err: any) {
// One broken icon should not stop the rest of the export
server._logger.warn(`Skipping icon of ${prototype.type} ${prototype.name}: ${err.message}`);
}
if (icon) {
count += 1;
}
@@ -484,6 +490,7 @@ async function exportLocale(
modZip = await loadZip(server, modVersions, zipCache, mod);
} catch (err: any) {
if (err.code === "ENOENT") {
server._logger.warn(`Skipping locale of ${mod}, its mod zip was not found`);
continue;
}
throw err;
@@ -635,6 +642,8 @@ async function buildExport(server: FactorioServer, zipCache: ZipCache) {
// For testing only
export const _createSheetState = createSheetState;
export const _exportIcons = exportIcons;
export const _exportLocale = exportLocale;
export const _loadSimpleIcon = loadSimpleIcon;
export const _loadLayeredIcon = loadLayeredIcon;
+43 -1
View File
@@ -2,7 +2,9 @@ import assert from "node:assert/strict";
import path from "node:path";
import { FactorioServer } from "@clusterio/host/dist/node/src/server.js";
import { _exportLocale, _loadLayeredIcon, _loadSimpleIcon } from "@clusterio/host/dist/node/src/export.js";
import {
_createSheetState, _exportIcons, _exportLocale, _loadLayeredIcon, _loadSimpleIcon,
} from "@clusterio/host/dist/node/src/export.js";
describe("host/src/export", function() {
@@ -12,7 +14,25 @@ describe("host/src/export", function() {
testServer = await FactorioServer.fromOptions(path.join("test", "file", "factorio"), writePath, {});
});
let warnings;
let originalLogger;
beforeEach(function() {
warnings = [];
originalLogger = testServer._logger;
testServer._logger = Object.create(originalLogger, { warn: { value: message => warnings.push(message) } });
});
afterEach(function() {
testServer._logger = originalLogger;
});
describe("exportLocale()", function() {
it("warns about mods without a zip and exports the rest", async function() {
const locale = await _exportLocale(
testServer, new Map([["missing_mod", "1.0.0"]]), new Map(), ["base", "missing_mod"], "en"
);
assert.equal(locale.size > 0, true);
assert.deepEqual(warnings, ["Skipping locale of missing_mod, its mod zip was not found"]);
});
it("returns a flattened mapping with locale definitions", async function() {
let locale = await _exportLocale(testServer, new Map(), new Map(), ["base"], "en");
assert.deepEqual(locale, new Map([
@@ -30,6 +50,28 @@ describe("host/src/export", function() {
// test-icon.png is a red 64px icon followed by a blue 32px mipmap
const red = 0xff0000ff;
describe("exportIcons()", function() {
it("skips icons that fail to load and exports the rest", async function() {
const state = await _createSheetState();
const icon = "__base__/graphics/icons/test-icon.png";
const prototypes = [
{ type: "item", name: "first", icon, icon_size: 64 },
{ type: "item", name: "no-layers", icons: [] },
{ type: "item", name: "unknown-mod", icon: "__not-a-mod__/graphics/icons/x.png", icon_size: 64 },
{ type: "item", name: "last", icon, icon_size: 64 },
];
const count = await _exportIcons(testServer, new Map(), new Map(), prototypes, "item", state);
assert.equal(count, 2);
assert.deepEqual(
state.metadata.item.map(entry => [entry.name, Boolean(entry.icon)]),
[["first", true], ["no-layers", false], ["unknown-mod", false], ["last", true]],
);
assert.equal(warnings.length, 2);
assert.match(warnings[0], /^Skipping icon of item no-layers: /);
assert.match(warnings[1], /^Skipping icon of item unknown-mod: Got path for unknown mod not-a-mod/);
});
});
describe("loadSimpleIcon()", function() {
it("defaults icon_size to 64", async function() {
const icon = await _loadSimpleIcon(