Initial commit
This commit is contained in:
23
node_modules/astro/dist/assets/build/generate.d.ts
generated
vendored
Normal file
23
node_modules/astro/dist/assets/build/generate.d.ts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { BuildPipeline } from '../../core/build/pipeline.js';
|
||||
import type { Logger } from '../../core/logger/core.js';
|
||||
import type { MapValue } from '../../type-utils.js';
|
||||
import type { AstroConfig } from '../../types/public/config.js';
|
||||
import type { AssetsGlobalStaticImagesList } from '../types.js';
|
||||
type AssetEnv = {
|
||||
logger: Logger;
|
||||
isSSR: boolean;
|
||||
count: {
|
||||
total: number;
|
||||
current: number;
|
||||
};
|
||||
useCache: boolean;
|
||||
assetsCacheDir: URL;
|
||||
serverRoot: URL;
|
||||
clientRoot: URL;
|
||||
imageConfig: AstroConfig['image'];
|
||||
assetsFolder: AstroConfig['build']['assets'];
|
||||
};
|
||||
export declare function prepareAssetsGenerationEnv(pipeline: BuildPipeline, totalCount: number): Promise<AssetEnv>;
|
||||
export declare function generateImagesForPath(originalFilePath: string, transformsAndPath: MapValue<AssetsGlobalStaticImagesList>, env: AssetEnv): Promise<void>;
|
||||
export declare function getStaticImageList(): AssetsGlobalStaticImagesList;
|
||||
export {};
|
||||
250
node_modules/astro/dist/assets/build/generate.js
generated
vendored
Normal file
250
node_modules/astro/dist/assets/build/generate.js
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
import fs, { readFileSync } from "node:fs";
|
||||
import { basename } from "node:path/posix";
|
||||
import colors from "piccolore";
|
||||
import { getOutDirWithinCwd } from "../../core/build/common.js";
|
||||
import { getTimeStat } from "../../core/build/util.js";
|
||||
import { AstroError } from "../../core/errors/errors.js";
|
||||
import { AstroErrorData } from "../../core/errors/index.js";
|
||||
import { isRemotePath, removeLeadingForwardSlash } from "../../core/path.js";
|
||||
import { getConfiguredImageService } from "../internal.js";
|
||||
import { isESMImportedImage } from "../utils/imageKind.js";
|
||||
import { loadRemoteImage, revalidateRemoteImage } from "./remote.js";
|
||||
async function prepareAssetsGenerationEnv(pipeline, totalCount) {
|
||||
const { config, logger, settings } = pipeline;
|
||||
let useCache = true;
|
||||
const assetsCacheDir = new URL("assets/", config.cacheDir);
|
||||
const count = { total: totalCount, current: 1 };
|
||||
try {
|
||||
await fs.promises.mkdir(assetsCacheDir, { recursive: true });
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
null,
|
||||
`An error was encountered while creating the cache directory. Proceeding without caching. Error: ${err}`
|
||||
);
|
||||
useCache = false;
|
||||
}
|
||||
const isServerOutput = settings.buildOutput === "server";
|
||||
let serverRoot, clientRoot;
|
||||
if (isServerOutput) {
|
||||
serverRoot = config.build.server;
|
||||
clientRoot = config.build.client;
|
||||
} else {
|
||||
serverRoot = getOutDirWithinCwd(config.outDir);
|
||||
clientRoot = config.outDir;
|
||||
}
|
||||
return {
|
||||
logger,
|
||||
isSSR: isServerOutput,
|
||||
count,
|
||||
useCache,
|
||||
assetsCacheDir,
|
||||
serverRoot,
|
||||
clientRoot,
|
||||
imageConfig: config.image,
|
||||
assetsFolder: config.build.assets
|
||||
};
|
||||
}
|
||||
function getFullImagePath(originalFilePath, env) {
|
||||
return new URL(removeLeadingForwardSlash(originalFilePath), env.serverRoot);
|
||||
}
|
||||
async function generateImagesForPath(originalFilePath, transformsAndPath, env) {
|
||||
let originalImage;
|
||||
for (const [_, transform] of transformsAndPath.transforms) {
|
||||
await generateImage(transform.finalPath, transform.transform);
|
||||
}
|
||||
if (!env.isSSR && transformsAndPath.originalSrcPath && !globalThis.astroAsset.referencedImages?.has(transformsAndPath.originalSrcPath)) {
|
||||
try {
|
||||
if (transformsAndPath.originalSrcPath) {
|
||||
env.logger.debug(
|
||||
"assets",
|
||||
`Deleting ${originalFilePath} as it's not referenced outside of image processing.`
|
||||
);
|
||||
await fs.promises.unlink(getFullImagePath(originalFilePath, env));
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
async function generateImage(filepath, options) {
|
||||
const timeStart = performance.now();
|
||||
const generationData = await generateImageInternal(filepath, options);
|
||||
const timeEnd = performance.now();
|
||||
const timeChange = getTimeStat(timeStart, timeEnd);
|
||||
const timeIncrease = `(+${timeChange})`;
|
||||
const statsText = generationData.cached !== "miss" ? generationData.cached === "hit" ? `(reused cache entry)` : `(revalidated cache entry)` : `(before: ${generationData.weight.before}kB, after: ${generationData.weight.after}kB)`;
|
||||
const count = `(${env.count.current}/${env.count.total})`;
|
||||
env.logger.info(
|
||||
null,
|
||||
` ${colors.green("\u25B6")} ${filepath} ${colors.dim(statsText)} ${colors.dim(timeIncrease)} ${colors.dim(count)}`
|
||||
);
|
||||
env.count.current++;
|
||||
}
|
||||
async function generateImageInternal(filepath, options) {
|
||||
const isLocalImage = isESMImportedImage(options.src);
|
||||
const finalFileURL = new URL("." + filepath, env.clientRoot);
|
||||
const finalFolderURL = new URL("./", finalFileURL);
|
||||
await fs.promises.mkdir(finalFolderURL, { recursive: true });
|
||||
const cacheFile = basename(filepath);
|
||||
const cachedFileURL = new URL(cacheFile, env.assetsCacheDir);
|
||||
const cacheMetaFile = cacheFile + ".json";
|
||||
const cachedMetaFileURL = new URL(cacheMetaFile, env.assetsCacheDir);
|
||||
try {
|
||||
if (isLocalImage) {
|
||||
await fs.promises.copyFile(cachedFileURL, finalFileURL, fs.constants.COPYFILE_FICLONE);
|
||||
return {
|
||||
cached: "hit"
|
||||
};
|
||||
} else {
|
||||
const JSONData = JSON.parse(readFileSync(cachedMetaFileURL, "utf-8"));
|
||||
if (!JSONData.expires) {
|
||||
try {
|
||||
await fs.promises.unlink(cachedFileURL);
|
||||
} catch {
|
||||
}
|
||||
await fs.promises.unlink(cachedMetaFileURL);
|
||||
throw new Error(
|
||||
`Malformed cache entry for ${filepath}, cache will be regenerated for this file.`
|
||||
);
|
||||
}
|
||||
if (JSONData.data) {
|
||||
const { data, ...meta } = JSONData;
|
||||
await Promise.all([
|
||||
fs.promises.writeFile(cachedFileURL, Buffer.from(data, "base64")),
|
||||
writeCacheMetaFile(cachedMetaFileURL, meta, env)
|
||||
]);
|
||||
}
|
||||
if (JSONData.expires > Date.now()) {
|
||||
await fs.promises.copyFile(cachedFileURL, finalFileURL, fs.constants.COPYFILE_FICLONE);
|
||||
return {
|
||||
cached: "hit"
|
||||
};
|
||||
}
|
||||
if (JSONData.etag || JSONData.lastModified) {
|
||||
try {
|
||||
const revalidatedData = await revalidateRemoteImage(options.src, {
|
||||
etag: JSONData.etag,
|
||||
lastModified: JSONData.lastModified
|
||||
});
|
||||
if (revalidatedData.data.length) {
|
||||
originalImage = revalidatedData;
|
||||
} else {
|
||||
await writeCacheMetaFile(cachedMetaFileURL, revalidatedData, env);
|
||||
await fs.promises.copyFile(
|
||||
cachedFileURL,
|
||||
finalFileURL,
|
||||
fs.constants.COPYFILE_FICLONE
|
||||
);
|
||||
return { cached: "revalidated" };
|
||||
}
|
||||
} catch (e) {
|
||||
env.logger.warn(
|
||||
null,
|
||||
`An error was encountered while revalidating a cached remote asset. Proceeding with stale cache. ${e}`
|
||||
);
|
||||
await fs.promises.copyFile(cachedFileURL, finalFileURL, fs.constants.COPYFILE_FICLONE);
|
||||
return { cached: "hit" };
|
||||
}
|
||||
}
|
||||
await fs.promises.unlink(cachedFileURL);
|
||||
await fs.promises.unlink(cachedMetaFileURL);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.code !== "ENOENT") {
|
||||
throw new Error(`An error was encountered while reading the cache file. Error: ${e}`);
|
||||
}
|
||||
}
|
||||
const originalImagePath = isLocalImage ? options.src.src : options.src;
|
||||
if (!originalImage) {
|
||||
originalImage = await loadImage(originalFilePath, env);
|
||||
}
|
||||
let resultData = {
|
||||
data: void 0,
|
||||
expires: originalImage.expires,
|
||||
etag: originalImage.etag,
|
||||
lastModified: originalImage.lastModified
|
||||
};
|
||||
const imageService = await getConfiguredImageService();
|
||||
try {
|
||||
resultData.data = (await imageService.transform(
|
||||
originalImage.data,
|
||||
{ ...options, src: originalImagePath },
|
||||
env.imageConfig
|
||||
)).data;
|
||||
} catch (e) {
|
||||
if (AstroError.is(e)) {
|
||||
throw e;
|
||||
}
|
||||
const error = new AstroError(
|
||||
{
|
||||
...AstroErrorData.CouldNotTransformImage,
|
||||
message: AstroErrorData.CouldNotTransformImage.message(originalFilePath)
|
||||
},
|
||||
{ cause: e }
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
if (env.useCache) {
|
||||
if (isLocalImage) {
|
||||
await fs.promises.writeFile(cachedFileURL, resultData.data);
|
||||
} else {
|
||||
await Promise.all([
|
||||
fs.promises.writeFile(cachedFileURL, resultData.data),
|
||||
writeCacheMetaFile(cachedMetaFileURL, resultData, env)
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
env.logger.warn(
|
||||
null,
|
||||
`An error was encountered while creating the cache directory. Proceeding without caching. Error: ${e}`
|
||||
);
|
||||
} finally {
|
||||
await fs.promises.writeFile(finalFileURL, resultData.data);
|
||||
}
|
||||
return {
|
||||
cached: "miss",
|
||||
weight: {
|
||||
// Divide by 1024 to get size in kilobytes
|
||||
before: Math.trunc(originalImage.data.byteLength / 1024),
|
||||
after: Math.trunc(Buffer.from(resultData.data).byteLength / 1024)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
async function writeCacheMetaFile(cachedMetaFileURL, resultData, env) {
|
||||
try {
|
||||
return await fs.promises.writeFile(
|
||||
cachedMetaFileURL,
|
||||
JSON.stringify({
|
||||
expires: resultData.expires,
|
||||
etag: resultData.etag,
|
||||
lastModified: resultData.lastModified
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
env.logger.warn(
|
||||
null,
|
||||
`An error was encountered while writing the cache file for a remote asset. Proceeding without caching this asset. Error: ${e}`
|
||||
);
|
||||
}
|
||||
}
|
||||
function getStaticImageList() {
|
||||
if (!globalThis?.astroAsset?.staticImages) {
|
||||
return /* @__PURE__ */ new Map();
|
||||
}
|
||||
return globalThis.astroAsset.staticImages;
|
||||
}
|
||||
async function loadImage(path, env) {
|
||||
if (isRemotePath(path)) {
|
||||
return await loadRemoteImage(path);
|
||||
}
|
||||
return {
|
||||
data: await fs.promises.readFile(getFullImagePath(path, env)),
|
||||
expires: 0
|
||||
};
|
||||
}
|
||||
export {
|
||||
generateImagesForPath,
|
||||
getStaticImageList,
|
||||
prepareAssetsGenerationEnv
|
||||
};
|
||||
30
node_modules/astro/dist/assets/build/remote.d.ts
generated
vendored
Normal file
30
node_modules/astro/dist/assets/build/remote.d.ts
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
export type RemoteCacheEntry = {
|
||||
data?: string;
|
||||
expires: number;
|
||||
etag?: string;
|
||||
lastModified?: string;
|
||||
};
|
||||
export declare function loadRemoteImage(src: string): Promise<{
|
||||
data: Buffer<ArrayBuffer>;
|
||||
expires: number;
|
||||
etag: string | undefined;
|
||||
lastModified: string | undefined;
|
||||
}>;
|
||||
/**
|
||||
* Revalidate a cached remote asset using its entity-tag or modified date.
|
||||
* Uses the [If-None-Match](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) and [If-Modified-Since](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since)
|
||||
* headers to check with the remote server if the cached version of a remote asset is still up to date.
|
||||
* The remote server may respond that the cached asset is still up-to-date if the entity-tag or modification time matches (304 Not Modified), or respond with an updated asset (200 OK)
|
||||
* @param src - url to remote asset
|
||||
* @param revalidationData - an object containing the stored Entity-Tag of the cached asset and/or the Last Modified time
|
||||
* @returns An ImageData object containing the asset data, a new expiry time, and the asset's etag. The data buffer will be empty if the asset was not modified.
|
||||
*/
|
||||
export declare function revalidateRemoteImage(src: string, revalidationData: {
|
||||
etag?: string;
|
||||
lastModified?: string;
|
||||
}): Promise<{
|
||||
data: Buffer<ArrayBuffer>;
|
||||
expires: number;
|
||||
etag: string | undefined;
|
||||
lastModified: string | undefined;
|
||||
}>;
|
||||
77
node_modules/astro/dist/assets/build/remote.js
generated
vendored
Normal file
77
node_modules/astro/dist/assets/build/remote.js
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
import CachePolicy from "http-cache-semantics";
|
||||
async function loadRemoteImage(src) {
|
||||
const req = new Request(src);
|
||||
const res = await fetch(req);
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Failed to load remote image ${src}. The request did not return a 200 OK response. (received ${res.status}))`
|
||||
);
|
||||
}
|
||||
const policy = new CachePolicy(webToCachePolicyRequest(req), webToCachePolicyResponse(res));
|
||||
const expires = policy.storable() ? policy.timeToLive() : 0;
|
||||
return {
|
||||
data: Buffer.from(await res.arrayBuffer()),
|
||||
expires: Date.now() + expires,
|
||||
etag: res.headers.get("Etag") ?? void 0,
|
||||
lastModified: res.headers.get("Last-Modified") ?? void 0
|
||||
};
|
||||
}
|
||||
async function revalidateRemoteImage(src, revalidationData) {
|
||||
const headers = {
|
||||
...revalidationData.etag && { "If-None-Match": revalidationData.etag },
|
||||
...revalidationData.lastModified && { "If-Modified-Since": revalidationData.lastModified }
|
||||
};
|
||||
const req = new Request(src, { headers, cache: "no-cache" });
|
||||
const res = await fetch(req);
|
||||
if (!res.ok && res.status !== 304) {
|
||||
throw new Error(
|
||||
`Failed to revalidate cached remote image ${src}. The request did not return a 200 OK / 304 NOT MODIFIED response. (received ${res.status} ${res.statusText})`
|
||||
);
|
||||
}
|
||||
const data = Buffer.from(await res.arrayBuffer());
|
||||
if (res.ok && !data.length) {
|
||||
return await loadRemoteImage(src);
|
||||
}
|
||||
const policy = new CachePolicy(
|
||||
webToCachePolicyRequest(req),
|
||||
webToCachePolicyResponse(
|
||||
res.ok ? res : new Response(null, { status: 200, headers: res.headers })
|
||||
)
|
||||
// 304 responses themselves are not cacheable, so just pretend to get the refreshed TTL
|
||||
);
|
||||
const expires = policy.storable() ? policy.timeToLive() : 0;
|
||||
return {
|
||||
data,
|
||||
expires: Date.now() + expires,
|
||||
// While servers should respond with the same headers as a 200 response, if they don't we should reuse the stored value
|
||||
etag: res.headers.get("Etag") ?? (res.ok ? void 0 : revalidationData.etag),
|
||||
lastModified: res.headers.get("Last-Modified") ?? (res.ok ? void 0 : revalidationData.lastModified)
|
||||
};
|
||||
}
|
||||
function webToCachePolicyRequest({ url, method, headers: _headers }) {
|
||||
let headers = {};
|
||||
try {
|
||||
headers = Object.fromEntries(_headers.entries());
|
||||
} catch {
|
||||
}
|
||||
return {
|
||||
method,
|
||||
url,
|
||||
headers
|
||||
};
|
||||
}
|
||||
function webToCachePolicyResponse({ status, headers: _headers }) {
|
||||
let headers = {};
|
||||
try {
|
||||
headers = Object.fromEntries(_headers.entries());
|
||||
} catch {
|
||||
}
|
||||
return {
|
||||
status,
|
||||
headers
|
||||
};
|
||||
}
|
||||
export {
|
||||
loadRemoteImage,
|
||||
revalidateRemoteImage
|
||||
};
|
||||
Reference in New Issue
Block a user