next-imagetools 0.0.0-stage → 0.0.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/LICENSE +21 -0
- package/README.md +489 -2
- package/client.d.ts +138 -0
- package/dist/asset-loader.cjs +38 -0
- package/dist/config-MI8A5KvL.d.mts +27 -0
- package/dist/config-MI8A5KvL.d.mts.map +1 -0
- package/dist/config.cjs +7 -0
- package/dist/config.d.cts +26 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +2 -0
- package/dist/config.mjs +8 -0
- package/dist/config.mjs.map +1 -0
- package/dist/core.cjs +9 -0
- package/dist/core.d.cts +1 -0
- package/dist/core.d.mts +1 -0
- package/dist/core.mjs +2 -0
- package/dist/index.cjs +87 -0
- package/dist/index.d.cts +46 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +48 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +88 -0
- package/dist/index.mjs.map +1 -0
- package/dist/loader.cjs +441 -0
- package/dist/shared-DNQObFGr.cjs +300 -0
- package/package.json +108 -3
package/dist/loader.cjs
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_shared = require("./shared-DNQObFGr.cjs");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
let node_path = require("node:path");
|
|
8
|
+
node_path = require_shared.__toESM(node_path, 1);
|
|
9
|
+
let node_url = require("node:url");
|
|
10
|
+
let imagetools_core = require("imagetools-core");
|
|
11
|
+
let picomatch = require("picomatch");
|
|
12
|
+
picomatch = require_shared.__toESM(picomatch, 1);
|
|
13
|
+
let sharp = require("sharp");
|
|
14
|
+
sharp = require_shared.__toESM(sharp, 1);
|
|
15
|
+
// src/directives.ts
|
|
16
|
+
/**
|
|
17
|
+
* Directives imagetools-core v12 reads, plus the ones this package handles itself. Kept by hand
|
|
18
|
+
* because core's own `parametersUsed` is always empty for the built-ins; drop it once core reports
|
|
19
|
+
* usage (JonasKruckenberg/imagetools#966).
|
|
20
|
+
*/
|
|
21
|
+
const KNOWN_DIRECTIVES = /* @__PURE__ */ new Set([
|
|
22
|
+
"w",
|
|
23
|
+
"h",
|
|
24
|
+
"aspect",
|
|
25
|
+
"allowUpscale",
|
|
26
|
+
"fit",
|
|
27
|
+
"position",
|
|
28
|
+
"kernel",
|
|
29
|
+
"format",
|
|
30
|
+
"quality",
|
|
31
|
+
"lossless",
|
|
32
|
+
"progressive",
|
|
33
|
+
"effort",
|
|
34
|
+
"background",
|
|
35
|
+
"blur",
|
|
36
|
+
"brightness",
|
|
37
|
+
"flatten",
|
|
38
|
+
"flip",
|
|
39
|
+
"flop",
|
|
40
|
+
"grayscale",
|
|
41
|
+
"hue",
|
|
42
|
+
"invert",
|
|
43
|
+
"median",
|
|
44
|
+
"normalize",
|
|
45
|
+
"noAutoOrient",
|
|
46
|
+
"rotate",
|
|
47
|
+
"saturation",
|
|
48
|
+
"tint",
|
|
49
|
+
"as",
|
|
50
|
+
"basePixels",
|
|
51
|
+
"top",
|
|
52
|
+
"right top",
|
|
53
|
+
"right",
|
|
54
|
+
"right bottom",
|
|
55
|
+
"bottom",
|
|
56
|
+
"left bottom",
|
|
57
|
+
"left",
|
|
58
|
+
"left top"
|
|
59
|
+
]);
|
|
60
|
+
/** Type-only markers (see client.d.ts), dropped before processing. */
|
|
61
|
+
const TYPE_MARKERS = ["imagetools", "imagetools-gallery"];
|
|
62
|
+
/** Outputs whose shape a marker picks: one value or an array, depending on the image count. */
|
|
63
|
+
const MARKED_OUTPUTS = /* @__PURE__ */ new Set([
|
|
64
|
+
"url",
|
|
65
|
+
"metadata",
|
|
66
|
+
"meta"
|
|
67
|
+
]);
|
|
68
|
+
/**
|
|
69
|
+
* Checks the type marker that ends a query against what the import gives. The client types pick an
|
|
70
|
+
* import's type by how its query ends, so a wrong marker makes TypeScript wrong about the value —
|
|
71
|
+
* `string` for what is really an array — with nothing else to say so.
|
|
72
|
+
*/
|
|
73
|
+
function checkMarker(query, as, count) {
|
|
74
|
+
const params = new URLSearchParams(query);
|
|
75
|
+
const last = [...params.keys()].at(-1);
|
|
76
|
+
if (last !== "imagetools" && last !== "imagetools-gallery") return void 0;
|
|
77
|
+
const [output = "url", keys] = (as ?? "url").split(":");
|
|
78
|
+
if (!MARKED_OUTPUTS.has(output)) return params.has("as") ? `as=${output} is typed without a marker; remove &${last}.` : `as=${output} comes from a preset or the defaults, so no marker can type this import; remove &${last} and declare its type, e.g. declare module '*preset=<name>'.`;
|
|
79
|
+
if (keys !== void 0) return `as=${output}:${keys} has no marker type, since its keys vary; remove &${last} and declare the import's type, or use as=${output} without keys.`;
|
|
80
|
+
if (last === "imagetools" && count > 1) return `the query gives ${count} images, an array, but &imagetools types it as a single value. End it with &imagetools-gallery.`;
|
|
81
|
+
if (last === "imagetools-gallery" && count === 1) return "the query gives one image, a single value, but &imagetools-gallery types it as an array. End it with &imagetools.";
|
|
82
|
+
}
|
|
83
|
+
/** Names people reach for that imagetools-core doesn't read, with what to use instead. */
|
|
84
|
+
const REPLACEMENTS = {
|
|
85
|
+
width: "w",
|
|
86
|
+
height: "h",
|
|
87
|
+
withoutEnlargement: "allowUpscale (images are never enlarged unless it is set)",
|
|
88
|
+
withoutReduction: "allowUpscale"
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Output formats there is an asset rule for. sharp encodes more (`jxl`, `jp2`, …), but an output
|
|
92
|
+
* without a rule would leave its internal import unresolved, so those are rejected up front.
|
|
93
|
+
*/
|
|
94
|
+
const OUTPUT_FORMATS = [
|
|
95
|
+
"jpg",
|
|
96
|
+
"jpeg",
|
|
97
|
+
"png",
|
|
98
|
+
"webp",
|
|
99
|
+
"avif",
|
|
100
|
+
"gif",
|
|
101
|
+
"tiff",
|
|
102
|
+
"heif"
|
|
103
|
+
];
|
|
104
|
+
const positiveInteger = (v) => /^\d+$/.test(v) && Number(v) > 0;
|
|
105
|
+
const integer = (v) => /^-?\d+$/.test(v);
|
|
106
|
+
const number = (v) => v.trim() !== "" && Number.isFinite(Number(v));
|
|
107
|
+
const flagOrNumber = (v) => v === "" || v === "true" || number(v);
|
|
108
|
+
/** Value checks for the directives imagetools-core would otherwise silently skip. */
|
|
109
|
+
const VALUE_RULES = {
|
|
110
|
+
format: [(v) => OUTPUT_FORMATS.includes(v), `one of ${OUTPUT_FORMATS.join(", ")}`],
|
|
111
|
+
w: [positiveInteger, "a positive integer"],
|
|
112
|
+
h: [positiveInteger, "a positive integer"],
|
|
113
|
+
quality: [(v) => integer(v) && Number(v) >= 1 && Number(v) <= 100, "an integer from 1 to 100"],
|
|
114
|
+
rotate: [integer, "an integer (degrees)"],
|
|
115
|
+
hue: [integer, "an integer (degrees)"],
|
|
116
|
+
saturation: [number, "a number"],
|
|
117
|
+
brightness: [number, "a number"],
|
|
118
|
+
blur: [flagOrNumber, "a number, or no value"],
|
|
119
|
+
median: [flagOrNumber, "a number, or no value"],
|
|
120
|
+
effort: [(v) => integer(v) || v === "min" || v === "max", "an integer, \"min\" or \"max\""],
|
|
121
|
+
aspect: [(v) => number(v) || /^\d+(\.\d+)?:\d+(\.\d+)?$/.test(v), "a number or a ratio like 16:9"]
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Checks resolved directives. `consumed` are keys the config file's `defaultDirectives` function
|
|
125
|
+
* read: custom keys like `?hero` are legitimate when that function acts on them.
|
|
126
|
+
*/
|
|
127
|
+
function checkDirectives(directives, outputFormats, consumed = /* @__PURE__ */ new Set()) {
|
|
128
|
+
const errors = [];
|
|
129
|
+
const warnings = [];
|
|
130
|
+
for (const [key, value] of directives) {
|
|
131
|
+
if (KNOWN_DIRECTIVES.has(key) || key in outputFormats || consumed.has(key)) {
|
|
132
|
+
const rule = VALUE_RULES[key];
|
|
133
|
+
if (!rule) continue;
|
|
134
|
+
const bad = value.split(";").filter((v) => !rule[0](v));
|
|
135
|
+
if (bad.length) errors.push(`${key}=${bad.join(";")} is not valid: ${key} takes ${rule[1]}.`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const near = closest(key, [...KNOWN_DIRECTIVES, ...Object.keys(REPLACEMENTS)]);
|
|
139
|
+
const instead = REPLACEMENTS[key] ?? (near && (REPLACEMENTS[near] ?? near));
|
|
140
|
+
warnings.push(`Unknown directive "${key}" has no effect.${instead ? ` Did you mean ${instead}?` : ""}`);
|
|
141
|
+
}
|
|
142
|
+
const as = directives.get("as")?.split(":")[0];
|
|
143
|
+
if (as !== void 0 && !(as in outputFormats)) {
|
|
144
|
+
const names = Object.keys(outputFormats);
|
|
145
|
+
const suggestion = closest(as, names);
|
|
146
|
+
errors.push(`Unknown output format as=${as}.${suggestion ? ` Did you mean as=${suggestion}?` : ""} Available: ${names.join(", ")}.`);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
errors,
|
|
150
|
+
warnings
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Drops empty values left by stray separators, so `format=webp;` means `format=webp` rather than a
|
|
155
|
+
* second image with no format (and an array instead of a single value).
|
|
156
|
+
*/
|
|
157
|
+
function dropEmptyValues(directives) {
|
|
158
|
+
for (const [key, value] of Array.from(directives.entries())) {
|
|
159
|
+
if (!value.includes(";")) continue;
|
|
160
|
+
directives.set(key, value.split(";").filter(Boolean).join(";"));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** The candidate closest to `input` by edit distance, if any is close enough to be a typo. */
|
|
164
|
+
function closest(input, candidates) {
|
|
165
|
+
let best;
|
|
166
|
+
let bestDistance = Infinity;
|
|
167
|
+
for (const candidate of candidates) {
|
|
168
|
+
const d = distance(input.toLowerCase(), candidate.toLowerCase());
|
|
169
|
+
if (d < bestDistance) [best, bestDistance] = [candidate, d];
|
|
170
|
+
}
|
|
171
|
+
return bestDistance <= Math.max(1, Math.floor(input.length / 3)) ? best : void 0;
|
|
172
|
+
}
|
|
173
|
+
/** Edit distance counting a swap of adjacent letters as one edit (`widht` → `width` is 1). */
|
|
174
|
+
function distance(a, b) {
|
|
175
|
+
const d = Array.from({ length: a.length + 1 }, (_row, i) => Array.from({ length: b.length + 1 }, (_cell, j) => i === 0 ? j : j === 0 ? i : 0));
|
|
176
|
+
for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
|
|
177
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
178
|
+
let best = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
|
|
179
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) best = Math.min(best, d[i - 2][j - 2] + 1);
|
|
180
|
+
d[i][j] = best;
|
|
181
|
+
}
|
|
182
|
+
return d[a.length][b.length];
|
|
183
|
+
}
|
|
184
|
+
// src/placeholder.ts
|
|
185
|
+
const BLUR_SIZE = 8;
|
|
186
|
+
const BLUR_QUALITY = 70;
|
|
187
|
+
/** A tiny WebP data URL, `BLUR_SIZE` pixels on its longer side; next/image blurs it with CSS. */
|
|
188
|
+
async function blurOf(image, width, height) {
|
|
189
|
+
const [w, h] = width >= height ? [BLUR_SIZE, Math.max(Math.round(height / width * BLUR_SIZE), 1)] : [Math.max(Math.round(width / height * BLUR_SIZE), 1), BLUR_SIZE];
|
|
190
|
+
return {
|
|
191
|
+
dataURL: `data:image/webp;base64,${(await image.clone().resize(w, h, { fit: "fill" }).webp({ quality: BLUR_QUALITY }).toBuffer()).toString("base64")}`,
|
|
192
|
+
width: w,
|
|
193
|
+
height: h
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** The image's dominant color as `#rrggbb`. */
|
|
197
|
+
async function colorOf(image) {
|
|
198
|
+
const { dominant } = await image.clone().stats();
|
|
199
|
+
return `#${[
|
|
200
|
+
dominant.r,
|
|
201
|
+
dominant.g,
|
|
202
|
+
dominant.b
|
|
203
|
+
].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
|
|
204
|
+
}
|
|
205
|
+
/** The variant a single-value result describes: the widest, as imagetools-core's `img` picks. */
|
|
206
|
+
const largest = (images) => images.reduce((a, b) => b.info.width > a.info.width ? b : a);
|
|
207
|
+
/**
|
|
208
|
+
* `as=next`: the shape of a Next.js static image import (`StaticImageData`), so the result goes
|
|
209
|
+
* straight into `<Image src={…} placeholder="blur" />`.
|
|
210
|
+
*/
|
|
211
|
+
const nextFormat = () => async (images) => {
|
|
212
|
+
const image = largest(images);
|
|
213
|
+
const { width, height } = image.info;
|
|
214
|
+
const blur = await blurOf(image.image, width, height);
|
|
215
|
+
return {
|
|
216
|
+
src: image.src,
|
|
217
|
+
width,
|
|
218
|
+
height,
|
|
219
|
+
blurDataURL: blur.dataURL,
|
|
220
|
+
blurWidth: blur.width,
|
|
221
|
+
blurHeight: blur.height
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
/** Parses the `placeholder` directive: bare or `blur` for a blurred image, `color` for a color. */
|
|
225
|
+
function placeholderKind(value) {
|
|
226
|
+
if (value === "" || value === "blur") return "blur";
|
|
227
|
+
if (value === "color") return "color";
|
|
228
|
+
throw new Error(`placeholder=${value} is not valid: placeholder takes no value, "blur" or "color".`);
|
|
229
|
+
}
|
|
230
|
+
async function placeholderOf(kind, image) {
|
|
231
|
+
return kind === "color" ? colorOf(image.image) : (await blurOf(image.image, image.info.width, image.info.height)).dataURL;
|
|
232
|
+
}
|
|
233
|
+
// src/loader.ts
|
|
234
|
+
const PLACEHOLDER = (i) => `__NIT_${i}__`;
|
|
235
|
+
const MIME_TYPES = {
|
|
236
|
+
jpg: "image/jpeg",
|
|
237
|
+
svg: "image/svg+xml"
|
|
238
|
+
};
|
|
239
|
+
const clamp = (s, n) => s.split(";").map((d) => Number.parseInt(d) <= n ? d : String(n)).join(";");
|
|
240
|
+
const SINGLE_VALUE_FORMATS = /* @__PURE__ */ new Set([
|
|
241
|
+
"srcset",
|
|
242
|
+
"img",
|
|
243
|
+
"picture",
|
|
244
|
+
"next"
|
|
245
|
+
]);
|
|
246
|
+
const matchers = /* @__PURE__ */ new Map();
|
|
247
|
+
const matcherFor = (globs) => {
|
|
248
|
+
const key = JSON.stringify(globs);
|
|
249
|
+
let match = matchers.get(key);
|
|
250
|
+
if (!match) matchers.set(key, match = (0, picomatch.default)(globs, { dot: true }));
|
|
251
|
+
return match;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Whether include/exclude let imagetools handle a file. Globs are matched against the path relative
|
|
255
|
+
* to the Next.js app (the loader's root context), not Turbopack's root, which in a monorepo is the
|
|
256
|
+
* workspace root — so `src/assets/**` means the same thing in either layout.
|
|
257
|
+
*/
|
|
258
|
+
function isHandled(relativePath, { include, exclude }) {
|
|
259
|
+
const file = relativePath.split(node_path.default.sep).join("/");
|
|
260
|
+
if (include?.length && !matcherFor(include)(file)) return false;
|
|
261
|
+
if (exclude?.length && matcherFor(exclude)(file)) return false;
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The directives an import ends up with, lowest precedence first: the `defaultDirectives` option,
|
|
266
|
+
* then the config file's `defaultDirectives` function, then the named preset, then the import's own
|
|
267
|
+
* query. Type-only markers (see client.d.ts) are dropped so a marked import shares cache entries
|
|
268
|
+
* and output files with the same import unmarked.
|
|
269
|
+
*/
|
|
270
|
+
function resolveDirectives(query, options, configDefaults) {
|
|
271
|
+
for (const marker of TYPE_MARKERS) query.delete(marker);
|
|
272
|
+
const presetName = query.get("preset");
|
|
273
|
+
query.delete("preset");
|
|
274
|
+
let preset;
|
|
275
|
+
if (presetName !== null) {
|
|
276
|
+
const definition = options.presets?.[presetName];
|
|
277
|
+
if (definition === void 0) {
|
|
278
|
+
const known = Object.keys(options.presets ?? {});
|
|
279
|
+
throw new Error(`Unknown imagetools preset "${presetName}". ` + (known.length ? `Defined presets: ${known.join(", ")}.` : "No presets are defined."));
|
|
280
|
+
}
|
|
281
|
+
preset = require_shared.toParams(definition);
|
|
282
|
+
}
|
|
283
|
+
const directives = new URLSearchParams();
|
|
284
|
+
for (const layer of [
|
|
285
|
+
require_shared.toParams(options.defaultDirectives),
|
|
286
|
+
configDefaults,
|
|
287
|
+
preset,
|
|
288
|
+
query
|
|
289
|
+
]) for (const [key, value] of layer ?? []) directives.set(key, value);
|
|
290
|
+
return directives;
|
|
291
|
+
}
|
|
292
|
+
/** Records the keys looked up through get/getAll/has. */
|
|
293
|
+
function trackReads(params, keys) {
|
|
294
|
+
for (const method of [
|
|
295
|
+
"get",
|
|
296
|
+
"getAll",
|
|
297
|
+
"has"
|
|
298
|
+
]) {
|
|
299
|
+
const original = params[method].bind(params);
|
|
300
|
+
Object.defineProperty(params, method, { value: (key, value) => {
|
|
301
|
+
keys.add(key);
|
|
302
|
+
return original(key, value);
|
|
303
|
+
} });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function uniqueByConfig(images) {
|
|
307
|
+
const seen = /* @__PURE__ */ new Set();
|
|
308
|
+
return images.filter((image) => {
|
|
309
|
+
const key = JSON.stringify(image.config);
|
|
310
|
+
if (seen.has(key)) return false;
|
|
311
|
+
seen.add(key);
|
|
312
|
+
return true;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function imagetoolsLoader() {
|
|
316
|
+
return require_shared.withContext(this, () => load.call(this));
|
|
317
|
+
}
|
|
318
|
+
async function load() {
|
|
319
|
+
const options = this.getOptions?.() ?? {};
|
|
320
|
+
const pathname = this.resourcePath;
|
|
321
|
+
if (!isHandled(node_path.default.relative(this.rootContext ?? process.cwd(), pathname), options)) return `export { default } from ${JSON.stringify(`./${node_path.default.basename(pathname)}`)};\n`;
|
|
322
|
+
const cacheDir = await require_shared.prepareCache(this, options);
|
|
323
|
+
const { config: userConfig, factories, hash: configHash } = await require_shared.loadConfig(this, options);
|
|
324
|
+
const builtins = {
|
|
325
|
+
...imagetools_core.builtinOutputFormats,
|
|
326
|
+
next: nextFormat
|
|
327
|
+
};
|
|
328
|
+
const outputFormats = userConfig.extendOutputFormats?.(builtins) ?? builtins;
|
|
329
|
+
let configDefaults;
|
|
330
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
331
|
+
if (userConfig.defaultDirectives) {
|
|
332
|
+
const url = (0, node_url.pathToFileURL)(pathname);
|
|
333
|
+
url.search = this.resourceQuery;
|
|
334
|
+
trackReads(url.searchParams, consumed);
|
|
335
|
+
let metadata;
|
|
336
|
+
configDefaults = await userConfig.defaultDirectives(url, () => metadata ??= (0, sharp.default)(pathname).metadata());
|
|
337
|
+
}
|
|
338
|
+
const directives = resolveDirectives(new URLSearchParams(this.resourceQuery), options, configDefaults);
|
|
339
|
+
const inline = directives.has("inline");
|
|
340
|
+
directives.delete("inline");
|
|
341
|
+
const placeholderValue = directives.get("placeholder");
|
|
342
|
+
directives.delete("placeholder");
|
|
343
|
+
const placeholder = placeholderValue === null ? void 0 : placeholderKind(placeholderValue);
|
|
344
|
+
dropEmptyValues(directives);
|
|
345
|
+
const problems = checkDirectives(directives, outputFormats, consumed);
|
|
346
|
+
for (const message of problems.warnings) require_shared.warn(this, message);
|
|
347
|
+
if (problems.errors.length) throw new Error(problems.errors.join(" "));
|
|
348
|
+
const source = await (0, node_fs_promises.readFile)(pathname);
|
|
349
|
+
const sourceHash = require_shared.hash([source]);
|
|
350
|
+
const meta = await (0, sharp.default)(source).metadata();
|
|
351
|
+
const upright = directives.has("noAutoOrient") ? meta : meta.autoOrient;
|
|
352
|
+
if (directives.get("allowUpscale") !== "true" && (directives.has("w") || directives.has("h"))) for (const [key, intrinsic] of [["w", upright.width], ["h", upright.height]]) {
|
|
353
|
+
const before = directives.get(key);
|
|
354
|
+
if (!before) continue;
|
|
355
|
+
const after = clamp(before, intrinsic);
|
|
356
|
+
directives.set(key, after);
|
|
357
|
+
if (new Set(after.split(";")).size < new Set(before.split(";")).size) require_shared.warn(this, `${key}=${before} goes beyond the source's ${intrinsic}px, so some variants come out identical. Remove the extra values, or add allowUpscale=true to scale up.`);
|
|
358
|
+
}
|
|
359
|
+
const entries = (0, imagetools_core.extractEntries)(directives);
|
|
360
|
+
const frames = require_shared.isAnimated(meta) ? meta.pages ?? 1 : 1;
|
|
361
|
+
if (frames > 1) {
|
|
362
|
+
const stills = (directives.get("format") ?? meta.format ?? "").split(";").filter((format) => format && !require_shared.ANIMATED_FORMATS.has(format));
|
|
363
|
+
if (stills.length) require_shared.warn(this, `the source has ${frames} frames, but ${stills.join(", ")} can't be animated, so only the first frame is kept. Use format=webp or format=gif to keep the animation.`);
|
|
364
|
+
}
|
|
365
|
+
const configs = userConfig.resolveConfigs?.(entries, outputFormats) ?? (0, imagetools_core.resolveConfigs)(entries, outputFormats);
|
|
366
|
+
const markerProblem = checkMarker(this.resourceQuery, directives.get("as"), configs.length);
|
|
367
|
+
if (markerProblem) throw new Error(markerProblem);
|
|
368
|
+
const logger = {
|
|
369
|
+
info: console.log,
|
|
370
|
+
warn: (m) => require_shared.warn(this, m),
|
|
371
|
+
error: (m) => {
|
|
372
|
+
throw new Error(m);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
const assetQueries = [];
|
|
376
|
+
const outputs = await Promise.all(configs.map(async (config, i) => {
|
|
377
|
+
const r = await require_shared.processImage({
|
|
378
|
+
source,
|
|
379
|
+
factories,
|
|
380
|
+
configHash,
|
|
381
|
+
sourceHash,
|
|
382
|
+
config,
|
|
383
|
+
searchParams: directives,
|
|
384
|
+
cacheDir,
|
|
385
|
+
logger,
|
|
386
|
+
removeMetadata: options.removeMetadata !== false
|
|
387
|
+
});
|
|
388
|
+
const ext = r.transforms.format === "jpeg" ? "jpg" : r.transforms.format;
|
|
389
|
+
let src;
|
|
390
|
+
if (inline) src = `data:${MIME_TYPES[ext ?? ""] ?? `image/${ext}`};base64,${r.data.toString("base64")}`;
|
|
391
|
+
else {
|
|
392
|
+
assetQueries[i] = `?nit=${sourceHash.slice(0, 12)}&nit_ext=${ext}&${new URLSearchParams(config)}`;
|
|
393
|
+
src = PLACEHOLDER(i);
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
src,
|
|
397
|
+
image: (0, sharp.default)(r.data),
|
|
398
|
+
config,
|
|
399
|
+
info: {
|
|
400
|
+
width: r.meta.width,
|
|
401
|
+
height: r.meta.height,
|
|
402
|
+
autoOriented: {
|
|
403
|
+
width: r.meta.width,
|
|
404
|
+
height: r.meta.height
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
transforms: r.transforms,
|
|
408
|
+
sharpMetadata: r.meta
|
|
409
|
+
};
|
|
410
|
+
}));
|
|
411
|
+
const [as, args] = directives.get("as")?.split(":") ?? [];
|
|
412
|
+
const outputFormat = as && outputFormats[as] || null;
|
|
413
|
+
const format = outputFormat ? outputFormat(args ? args.split(";") : void 0) : (0, imagetools_core.urlFormat)();
|
|
414
|
+
const formatted = as && SINGLE_VALUE_FORMATS.has(as) ? uniqueByConfig(outputs) : outputs;
|
|
415
|
+
const result = await format(formatted);
|
|
416
|
+
if (placeholder) {
|
|
417
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) throw new Error("placeholder needs a single object result: use as=img, as=picture, as=metadata or as=next, with one image per format.");
|
|
418
|
+
result.placeholder = await placeholderOf(placeholder, largest(formatted));
|
|
419
|
+
}
|
|
420
|
+
const json = JSON.stringify(result).replaceAll(/__NIT_(\d+)__/g, "\" + __nit_url(__nit_$1) + \"");
|
|
421
|
+
const self = `./${node_path.default.basename(pathname)}`;
|
|
422
|
+
return `${assetQueries.map((q, i) => q === void 0 ? "" : `import __nit_${i} from ${JSON.stringify(self + q)};`).filter(Boolean).join("\n")}
|
|
423
|
+
const __nit_url = (m) => encodeURI(m && typeof m === 'object' ? m.src : m);
|
|
424
|
+
const __nit_value = ${json};
|
|
425
|
+
export default __nit_value;
|
|
426
|
+
${(options.namedExports === false ? [] : namedExportKeys(result)).map((key) => `export const ${key} = __nit_value[${JSON.stringify(key)}];`).join("\n")}
|
|
427
|
+
`;
|
|
428
|
+
}
|
|
429
|
+
const RESERVED = new Set("await break case catch class const continue debugger default delete do else enum export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield".split(" "));
|
|
430
|
+
/**
|
|
431
|
+
* Keys of an object result (`as=metadata`, `as=img`, `as=picture`) to also export by name, so
|
|
432
|
+
* `import { width, src } from './a.jpg?as=metadata'` works as it does in vite-imagetools. Arrays
|
|
433
|
+
* and strings have no keys to export.
|
|
434
|
+
*/
|
|
435
|
+
function namedExportKeys(result) {
|
|
436
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) return [];
|
|
437
|
+
return Object.keys(result).filter((k) => /^[A-Za-z_$][\w$]*$/.test(k) && !RESERVED.has(k));
|
|
438
|
+
}
|
|
439
|
+
const raw = true;
|
|
440
|
+
exports.default = imagetoolsLoader;
|
|
441
|
+
exports.raw = raw;
|