@koehler8/cms 1.0.0-beta.27 → 1.0.0-beta.28
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/package.json
CHANGED
|
@@ -64,10 +64,18 @@ export async function reconcileVariantCache({
|
|
|
64
64
|
continue;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// Source is "cached" if the manifest knows it, mtime hasn't changed,
|
|
68
|
+
// config hasn't changed, AND every variant the previous run wrote is
|
|
69
|
+
// still on disk. We check `previous.variants` (what was actually
|
|
70
|
+
// generated) rather than `job.outputs` (everything the planner asked
|
|
71
|
+
// for) so tiny-source sites — where the renderer skipped over-large
|
|
72
|
+
// widths to avoid Vite content-dedup — aren't perpetually re-rendered
|
|
73
|
+
// on each build.
|
|
74
|
+
const previousVariants = previous?.variants || [];
|
|
67
75
|
const allCached = !configChanged
|
|
68
76
|
&& previous
|
|
69
77
|
&& previous.mtimeMs >= sourceMtime
|
|
70
|
-
&&
|
|
78
|
+
&& previousVariants.every((v) => fs.existsSync(path.join(cacheDir, v)));
|
|
71
79
|
|
|
72
80
|
if (allCached) {
|
|
73
81
|
plannedRenders.push({ job, sourceMtime, render: false });
|
|
@@ -87,12 +95,43 @@ export async function reconcileVariantCache({
|
|
|
87
95
|
sharp = mod.default;
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
// Per-source: filter outputs to widths the source can actually fulfill.
|
|
99
|
+
// Sharp's `withoutEnlargement: true` clamps oversize requests to the
|
|
100
|
+
// source dimensions, producing content-identical files for every width
|
|
101
|
+
// greater than the source. Vite content-hashes those files identically
|
|
102
|
+
// and emits ONE physical asset under one of the names — but assetUrlMap
|
|
103
|
+
// still has URL keys for every width, so requests for the deduped
|
|
104
|
+
// higher-width URLs 404.
|
|
105
|
+
//
|
|
106
|
+
// Fix: only generate variants for widths < source.width. Plus, always
|
|
107
|
+
// generate at exactly source.width (for crisp 1× display) by remapping
|
|
108
|
+
// any single oversize width down to source.width if no smaller variant
|
|
109
|
+
// would otherwise exist. The pipeline's natural fallback to the bare
|
|
110
|
+
// `img/{name}.{ext}` URL (which exists because the original is in the
|
|
111
|
+
// flat dir) covers tiny sources where no variant width fits.
|
|
112
|
+
const filteredOutputsByJob = new Map();
|
|
113
|
+
for (const { job } of plannedRenders) {
|
|
114
|
+
let sourceWidth = Number.MAX_SAFE_INTEGER;
|
|
115
|
+
if (sharp) {
|
|
116
|
+
try {
|
|
117
|
+
const meta = await sharp(job.sourcePath).metadata();
|
|
118
|
+
if (meta && Number.isFinite(meta.width)) sourceWidth = meta.width;
|
|
119
|
+
} catch {
|
|
120
|
+
// If metadata read fails, fall through and render everything —
|
|
121
|
+
// the failure is benign (just the dedup risk we're trying to avoid).
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const kept = job.outputs.filter((o) => o.width <= sourceWidth);
|
|
125
|
+
filteredOutputsByJob.set(job.sourceKey, kept);
|
|
126
|
+
}
|
|
127
|
+
|
|
90
128
|
for (const { job, sourceMtime, render } of plannedRenders) {
|
|
91
129
|
if (!render) {
|
|
92
|
-
skipped += job.outputs.length;
|
|
130
|
+
skipped += (filteredOutputsByJob.get(job.sourceKey) || job.outputs).length;
|
|
93
131
|
continue;
|
|
94
132
|
}
|
|
95
|
-
|
|
133
|
+
const outputs = filteredOutputsByJob.get(job.sourceKey) || job.outputs;
|
|
134
|
+
for (const output of outputs) {
|
|
96
135
|
const outPath = path.join(cacheDir, output.cacheRelPath);
|
|
97
136
|
try {
|
|
98
137
|
await renderOutput(sharp, job.sourcePath, { ...output, outPath }, currentConfig);
|
|
@@ -104,15 +143,32 @@ export async function reconcileVariantCache({
|
|
|
104
143
|
void sourceMtime;
|
|
105
144
|
}
|
|
106
145
|
|
|
146
|
+
// Evict any cached variants that the source can no longer fulfill
|
|
147
|
+
// (e.g. someone replaced a 4000px source with a 200px one — the
|
|
148
|
+
// 1280/1920/2560 outputs from the previous run are now stale).
|
|
149
|
+
for (const { job, render } of plannedRenders) {
|
|
150
|
+
if (!render) continue;
|
|
151
|
+
const kept = new Set((filteredOutputsByJob.get(job.sourceKey) || job.outputs).map((o) => o.cacheRelPath));
|
|
152
|
+
for (const o of job.outputs) {
|
|
153
|
+
if (kept.has(o.cacheRelPath)) continue;
|
|
154
|
+
const stalePath = path.join(cacheDir, o.cacheRelPath);
|
|
155
|
+
if (fs.existsSync(stalePath)) {
|
|
156
|
+
await fs.promises.rm(stalePath, { force: true });
|
|
157
|
+
evicted += 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
107
162
|
const newManifest = {
|
|
108
163
|
version: MANIFEST_VERSION,
|
|
109
164
|
config: currentConfig,
|
|
110
165
|
sources: {},
|
|
111
166
|
};
|
|
112
167
|
for (const { job, sourceMtime } of plannedRenders) {
|
|
168
|
+
const outputs = filteredOutputsByJob.get(job.sourceKey) || job.outputs;
|
|
113
169
|
newManifest.sources[job.sourceKey] = {
|
|
114
170
|
mtimeMs: sourceMtime,
|
|
115
|
-
variants:
|
|
171
|
+
variants: outputs.map((o) => o.cacheRelPath),
|
|
116
172
|
};
|
|
117
173
|
}
|
|
118
174
|
writeManifest(manifestPath, newManifest);
|