@riebeckite/plugin-svg-optimizer 0.0.41 → 0.0.42

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.
Files changed (2) hide show
  1. package/dist/index.js +150 -129
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -26,90 +26,14 @@ function svgOptimizer(options = {}) {
26
26
  svgo: SVGO_VERSION
27
27
  }),
28
28
  options,
29
- transformAssets: async ({ assets, readAsset, cache, logger, tracer }) => {
29
+ transformAssets: async (context) => {
30
30
  stats = emptyStats();
31
31
  if (resolved.keepOriginal) return [];
32
32
  const outputs = [];
33
- for (const asset of assets) {
33
+ for (const asset of context.assets) {
34
34
  if (extensionOf(asset.path) !== "svg") continue;
35
- stats.inputs += 1;
36
- if (asset.size !== void 0 && asset.size > DEFAULT_MAX_INPUT_BYTES) {
37
- stats.skipped.push({ path: asset.path, reason: "oversized" });
38
- logger.warn(`Skipped oversized SVG asset: ${asset.path}`);
39
- continue;
40
- }
41
- const input = await readAsset(asset.path);
42
- if (!input || input.byteLength > DEFAULT_MAX_INPUT_BYTES) {
43
- stats.skipped.push({ path: asset.path, reason: "unreadable" });
44
- logger.warn(
45
- `Skipped oversized or unreadable SVG asset: ${asset.path}`
46
- );
47
- continue;
48
- }
49
- const cacheKey = assetCacheKey(asset.path, input);
50
- const cached = await cache.get(cacheKey);
51
- if (isCachedSvgTransform(cached)) {
52
- stats.cacheHits += 1;
53
- tracer.event("svg-transform.cache-hit", { path: asset.path });
54
- if (cached.content !== null) {
55
- const output = outputFor(asset.path, cached.content);
56
- stats.optimized += 1;
57
- stats.savedBytes += input.byteLength - output.content.byteLength;
58
- outputs.push(output);
59
- } else {
60
- stats.noReduction += 1;
61
- }
62
- continue;
63
- }
64
- stats.cacheMisses += 1;
65
- const source = decode(input);
66
- if (source === null) {
67
- stats.skipped.push({ path: asset.path, reason: "invalid UTF-8" });
68
- logger.warn(
69
- `Skipped SVG asset that is not valid UTF-8: ${asset.path}`
70
- );
71
- await cache.set(cacheKey, { content: null });
72
- continue;
73
- }
74
- const unsafe = unsafeReason(source);
75
- if (unsafe) {
76
- stats.skipped.push({ path: asset.path, reason: unsafe });
77
- logger.warn(
78
- `Skipped SVG asset containing ${unsafe}: ${asset.path}. Optimization is not sanitization.`
79
- );
80
- await cache.set(cacheKey, { content: null });
81
- continue;
82
- }
83
- let optimized;
84
- try {
85
- optimized = optimize(source, config).data;
86
- } catch {
87
- stats.skipped.push({ path: asset.path, reason: "optimize failed" });
88
- logger.warn(
89
- `Skipped SVG asset that could not be optimized: ${asset.path}`
90
- );
91
- await cache.set(cacheKey, { content: null });
92
- continue;
93
- }
94
- const content = Buffer.from(optimized, "utf8");
95
- if (content.byteLength >= input.byteLength) {
96
- stats.noReduction += 1;
97
- tracer.event("svg-transform.no-reduction", { path: asset.path });
98
- await cache.set(cacheKey, { content: null });
99
- continue;
100
- }
101
- await cache.set(cacheKey, {
102
- content: content.toString("base64")
103
- });
104
- tracer.event("svg-transform.run", { path: asset.path });
105
- stats.optimized += 1;
106
- stats.savedBytes += input.byteLength - content.byteLength;
107
- outputs.push({
108
- sourcePath: asset.path,
109
- path: asset.path,
110
- content,
111
- mimeType: MIME_TYPE
112
- });
35
+ const output = await optimizeSvgAsset(asset, context, config, stats);
36
+ if (output) outputs.push(output);
113
37
  }
114
38
  return outputs;
115
39
  },
@@ -124,60 +48,157 @@ function svgOptimizer(options = {}) {
124
48
  ja: "最適化結果、キャッシュ利用、スキップしたアセットを表示します。"
125
49
  },
126
50
  order: 23,
127
- collect: () => resolved.keepOriginal ? {
128
- kind: "key-value",
129
- items: [
130
- {
131
- key: { en: "Status", ja: "状態" },
132
- value: "disabled (keepOriginal)"
133
- }
134
- ]
135
- } : {
136
- kind: "key-value",
137
- items: [
138
- { key: { en: "Inputs", ja: "入力" }, value: stats.inputs },
139
- {
140
- key: { en: "Optimized", ja: "最適化" },
141
- value: stats.optimized
142
- },
143
- {
144
- key: { en: "No reduction", ja: "削減なし" },
145
- value: stats.noReduction
146
- },
147
- {
148
- key: { en: "Cache hits", ja: "キャッシュヒット" },
149
- value: stats.cacheHits
150
- },
151
- {
152
- key: { en: "Cache misses", ja: "キャッシュミス" },
153
- value: stats.cacheMisses
154
- },
155
- {
156
- key: { en: "Saved", ja: "削減量" },
157
- value: formatBytes(stats.savedBytes)
158
- },
159
- {
160
- key: { en: "Skipped", ja: "スキップ" },
161
- value: stats.skipped.length
162
- },
163
- ...stats.skipped.length > 0 ? [
164
- {
165
- key: {
166
- en: "Skipped assets",
167
- ja: "スキップしたアセット"
168
- },
169
- value: stats.skipped.slice(0, 20).map(
170
- (skipped) => `${skipped.path} (${skipped.reason})`
171
- ).join("; ")
172
- }
173
- ] : []
174
- ]
175
- }
51
+ collect: () => collectInspectorItems(resolved, stats)
176
52
  }
177
53
  ]
178
54
  }
179
55
  });
180
56
  }
57
+ async function optimizeSvgAsset(asset, context, config, stats) {
58
+ stats.inputs += 1;
59
+ const input = await readSvgInput(asset, context, stats);
60
+ if (!input) return void 0;
61
+ const cacheKey = assetCacheKey(asset.path, input);
62
+ const cached = await context.cache.get(cacheKey);
63
+ if (isCachedSvgTransform(cached))
64
+ return applyCachedSvg(cached, asset, input, context, stats);
65
+ stats.cacheMisses += 1;
66
+ const source = decode(input);
67
+ if (source === null)
68
+ return skipSvgAsset(
69
+ stats,
70
+ context,
71
+ cacheKey,
72
+ asset.path,
73
+ "invalid UTF-8",
74
+ `Skipped SVG asset that is not valid UTF-8: ${asset.path}`
75
+ );
76
+ const unsafe = unsafeReason(source);
77
+ if (unsafe)
78
+ return skipSvgAsset(
79
+ stats,
80
+ context,
81
+ cacheKey,
82
+ asset.path,
83
+ unsafe,
84
+ `Skipped SVG asset containing ${unsafe}: ${asset.path}. Optimization is not sanitization.`
85
+ );
86
+ let optimized;
87
+ try {
88
+ optimized = optimize(source, config).data;
89
+ } catch {
90
+ return skipSvgAsset(
91
+ stats,
92
+ context,
93
+ cacheKey,
94
+ asset.path,
95
+ "optimize failed",
96
+ `Skipped SVG asset that could not be optimized: ${asset.path}`
97
+ );
98
+ }
99
+ const content = Buffer.from(optimized, "utf8");
100
+ if (content.byteLength >= input.byteLength) {
101
+ stats.noReduction += 1;
102
+ context.tracer.event("svg-transform.no-reduction", { path: asset.path });
103
+ await context.cache.set(cacheKey, { content: null });
104
+ return void 0;
105
+ }
106
+ await context.cache.set(cacheKey, { content: content.toString("base64") });
107
+ context.tracer.event("svg-transform.run", { path: asset.path });
108
+ stats.optimized += 1;
109
+ stats.savedBytes += input.byteLength - content.byteLength;
110
+ return {
111
+ sourcePath: asset.path,
112
+ path: asset.path,
113
+ content,
114
+ mimeType: MIME_TYPE
115
+ };
116
+ }
117
+ async function readSvgInput(asset, context, stats) {
118
+ if (asset.size !== void 0 && asset.size > DEFAULT_MAX_INPUT_BYTES) {
119
+ stats.skipped.push({ path: asset.path, reason: "oversized" });
120
+ context.logger.warn(`Skipped oversized SVG asset: ${asset.path}`);
121
+ return void 0;
122
+ }
123
+ const input = await context.readAsset(asset.path);
124
+ if (!input || input.byteLength > DEFAULT_MAX_INPUT_BYTES) {
125
+ stats.skipped.push({ path: asset.path, reason: "unreadable" });
126
+ context.logger.warn(
127
+ `Skipped oversized or unreadable SVG asset: ${asset.path}`
128
+ );
129
+ return void 0;
130
+ }
131
+ return input;
132
+ }
133
+ function applyCachedSvg(cached, asset, input, context, stats) {
134
+ stats.cacheHits += 1;
135
+ context.tracer.event("svg-transform.cache-hit", { path: asset.path });
136
+ if (cached.content === null) {
137
+ stats.noReduction += 1;
138
+ return void 0;
139
+ }
140
+ const output = outputFor(asset.path, cached.content);
141
+ stats.optimized += 1;
142
+ stats.savedBytes += input.byteLength - output.content.byteLength;
143
+ return output;
144
+ }
145
+ async function skipSvgAsset(stats, context, cacheKey, path, reason, message) {
146
+ stats.skipped.push({ path, reason });
147
+ context.logger.warn(message);
148
+ await context.cache.set(cacheKey, { content: null });
149
+ return void 0;
150
+ }
151
+ function collectInspectorItems(resolved, stats) {
152
+ if (resolved.keepOriginal)
153
+ return {
154
+ kind: "key-value",
155
+ items: [
156
+ {
157
+ key: { en: "Status", ja: "状態" },
158
+ value: "disabled (keepOriginal)"
159
+ }
160
+ ]
161
+ };
162
+ return {
163
+ kind: "key-value",
164
+ items: [
165
+ { key: { en: "Inputs", ja: "入力" }, value: stats.inputs },
166
+ {
167
+ key: { en: "Optimized", ja: "最適化" },
168
+ value: stats.optimized
169
+ },
170
+ {
171
+ key: { en: "No reduction", ja: "削減なし" },
172
+ value: stats.noReduction
173
+ },
174
+ {
175
+ key: { en: "Cache hits", ja: "キャッシュヒット" },
176
+ value: stats.cacheHits
177
+ },
178
+ {
179
+ key: { en: "Cache misses", ja: "キャッシュミス" },
180
+ value: stats.cacheMisses
181
+ },
182
+ {
183
+ key: { en: "Saved", ja: "削減量" },
184
+ value: formatBytes(stats.savedBytes)
185
+ },
186
+ {
187
+ key: { en: "Skipped", ja: "スキップ" },
188
+ value: stats.skipped.length
189
+ },
190
+ ...stats.skipped.length > 0 ? [
191
+ {
192
+ key: {
193
+ en: "Skipped assets",
194
+ ja: "スキップしたアセット"
195
+ },
196
+ value: stats.skipped.slice(0, 20).map((skipped) => `${skipped.path} (${skipped.reason})`).join("; ")
197
+ }
198
+ ] : []
199
+ ]
200
+ };
201
+ }
181
202
  function svgoConfig(options) {
182
203
  const overrides = {
183
204
  cleanupIds: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-svg-optimizer",
3
- "version": "0.0.41",
3
+ "version": "0.0.42",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,14 +14,14 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@riebeckite/core": "0.0.41",
18
- "svgo": "^4.1.0"
17
+ "svgo": "^4.1.0",
18
+ "@riebeckite/core": "0.0.42"
19
19
  },
20
20
  "devDependencies": {
21
- "@riebeckite/plugin-image-optimizer": "0.0.41",
22
- "@riebeckite/plugin-obsidian-markdown": "0.0.41",
23
21
  "sharp": "^0.35.5",
24
- "tsx": "^4.22.4"
22
+ "tsx": "^4.22.4",
23
+ "@riebeckite/plugin-image-optimizer": "0.0.42",
24
+ "@riebeckite/plugin-obsidian-markdown": "0.0.42"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"