@stati/core 1.27.1 → 1.28.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/dist/config/errors.d.ts +10 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +10 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +15 -2
- package/dist/constants.d.ts +26 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +23 -0
- package/dist/core/build.d.ts.map +1 -1
- package/dist/core/build.js +98 -26
- package/dist/core/isg/builder.d.ts +21 -6
- package/dist/core/isg/builder.d.ts.map +1 -1
- package/dist/core/isg/builder.js +59 -17
- package/dist/core/isg/deps.d.ts +23 -2
- package/dist/core/isg/deps.d.ts.map +1 -1
- package/dist/core/isg/deps.js +54 -8
- package/dist/core/isg/hash.d.ts +10 -0
- package/dist/core/isg/hash.d.ts.map +1 -1
- package/dist/core/isg/hash.js +14 -0
- package/dist/core/isg/index.d.ts +2 -1
- package/dist/core/isg/index.d.ts.map +1 -1
- package/dist/core/isg/index.js +1 -1
- package/dist/core/isg/manifest.js +8 -0
- package/dist/core/markdown.d.ts +10 -1
- package/dist/core/markdown.d.ts.map +1 -1
- package/dist/core/markdown.js +13 -3
- package/dist/core/templates.d.ts +12 -1
- package/dist/core/templates.d.ts.map +1 -1
- package/dist/core/templates.js +61 -6
- package/dist/core/utils/fs.utils.d.ts +21 -1
- package/dist/core/utils/fs.utils.d.ts.map +1 -1
- package/dist/core/utils/fs.utils.js +61 -0
- package/dist/core/utils/index.d.ts +1 -1
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core/utils/index.js +1 -1
- package/dist/core/utils/template-discovery.utils.d.ts +8 -6
- package/dist/core/utils/template-discovery.utils.d.ts.map +1 -1
- package/dist/core/utils/template-discovery.utils.js +43 -12
- package/dist/images/config.d.ts +52 -0
- package/dist/images/config.d.ts.map +1 -0
- package/dist/images/config.js +200 -0
- package/dist/images/index.d.ts +15 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +11 -0
- package/dist/images/manifest.d.ts +36 -0
- package/dist/images/manifest.d.ts.map +1 -0
- package/dist/images/manifest.js +69 -0
- package/dist/images/markdown-rule.d.ts +26 -0
- package/dist/images/markdown-rule.d.ts.map +1 -0
- package/dist/images/markdown-rule.js +60 -0
- package/dist/images/markup.d.ts +39 -0
- package/dist/images/markup.d.ts.map +1 -0
- package/dist/images/markup.js +72 -0
- package/dist/images/og.d.ts +52 -0
- package/dist/images/og.d.ts.map +1 -0
- package/dist/images/og.js +165 -0
- package/dist/images/plan.d.ts +101 -0
- package/dist/images/plan.d.ts.map +1 -0
- package/dist/images/plan.js +128 -0
- package/dist/images/provider.d.ts +35 -0
- package/dist/images/provider.d.ts.map +1 -0
- package/dist/images/provider.js +70 -0
- package/dist/images/resolve.d.ts +45 -0
- package/dist/images/resolve.d.ts.map +1 -0
- package/dist/images/resolve.js +51 -0
- package/dist/images/runtime.d.ts +258 -0
- package/dist/images/runtime.d.ts.map +1 -0
- package/dist/images/runtime.js +659 -0
- package/dist/images/sources.d.ts +47 -0
- package/dist/images/sources.d.ts.map +1 -0
- package/dist/images/sources.js +97 -0
- package/dist/images/utils/image-probe.utils.d.ts +30 -0
- package/dist/images/utils/image-probe.utils.d.ts.map +1 -0
- package/dist/images/utils/image-probe.utils.js +307 -0
- package/dist/images/utils/index.d.ts +7 -0
- package/dist/images/utils/index.d.ts.map +1 -0
- package/dist/images/utils/index.js +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/metrics/noop.d.ts.map +1 -1
- package/dist/metrics/noop.js +3 -0
- package/dist/metrics/recorder.d.ts.map +1 -1
- package/dist/metrics/recorder.js +3 -0
- package/dist/metrics/types.d.ts +8 -0
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/seo/auto-inject.d.ts +3 -0
- package/dist/seo/auto-inject.d.ts.map +1 -1
- package/dist/seo/auto-inject.js +1 -0
- package/dist/seo/generator.d.ts +24 -0
- package/dist/seo/generator.d.ts.map +1 -1
- package/dist/seo/generator.js +65 -1
- package/dist/seo/index.d.ts +1 -1
- package/dist/seo/index.d.ts.map +1 -1
- package/dist/seo/index.js +1 -1
- package/dist/types/config.d.ts +5 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/content.d.ts +21 -2
- package/dist/types/content.d.ts.map +1 -1
- package/dist/types/images.d.ts +185 -0
- package/dist/types/images.d.ts.map +1 -0
- package/dist/types/images.js +8 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/isg.d.ts +7 -0
- package/dist/types/isg.d.ts.map +1 -1
- package/dist/types/seo.d.ts +9 -0
- package/dist/types/seo.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decides which files to generate for an image and what to call them.
|
|
3
|
+
* Names come from inputs, not from encoded bytes, so page HTML can be written before
|
|
4
|
+
* encoding finishes.
|
|
5
|
+
* @module images/plan
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { basename, extname, posix } from 'node:path';
|
|
9
|
+
import { slugify } from '../core/utils/index.js';
|
|
10
|
+
import { IMAGES_OUTPUT_DIR } from '../constants.js';
|
|
11
|
+
/** Length of the hash in generated file names. */
|
|
12
|
+
const NAME_HASH_LENGTH = 8;
|
|
13
|
+
/** MIME types for `<source type>`. */
|
|
14
|
+
const MIME_TYPES = {
|
|
15
|
+
avif: 'image/avif',
|
|
16
|
+
webp: 'image/webp',
|
|
17
|
+
jpeg: 'image/jpeg',
|
|
18
|
+
png: 'image/png',
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Picks the widths to generate for an image.
|
|
22
|
+
* Configured widths up to the image's own width are kept, and the image's own width is
|
|
23
|
+
* added when it is below the largest configured width. Images are never enlarged.
|
|
24
|
+
*
|
|
25
|
+
* @param intrinsicWidth - The image's display width
|
|
26
|
+
* @param configured - Candidate widths, ascending
|
|
27
|
+
* @returns Widths to generate, ascending
|
|
28
|
+
*/
|
|
29
|
+
export function planWidths(intrinsicWidth, configured) {
|
|
30
|
+
const widths = configured.filter((width) => width <= intrinsicWidth);
|
|
31
|
+
const largest = configured.length > 0 ? Math.max(...configured) : intrinsicWidth;
|
|
32
|
+
if (intrinsicWidth < largest && !widths.includes(intrinsicWidth)) {
|
|
33
|
+
widths.push(intrinsicWidth);
|
|
34
|
+
}
|
|
35
|
+
if (widths.length === 0) {
|
|
36
|
+
widths.push(intrinsicWidth);
|
|
37
|
+
}
|
|
38
|
+
return [...new Set(widths)].sort((a, b) => a - b);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The format of the `<img>` fallback for a source format: the source's own format, except
|
|
42
|
+
* GIF, which becomes PNG.
|
|
43
|
+
*/
|
|
44
|
+
export function fallbackFormatFor(format) {
|
|
45
|
+
return format === 'gif' ? 'png' : format;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* File extension for a format.
|
|
49
|
+
*/
|
|
50
|
+
export function extensionFor(format) {
|
|
51
|
+
return format === 'jpeg' ? 'jpg' : format;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* MIME type for a generated format.
|
|
55
|
+
*/
|
|
56
|
+
export function mimeTypeFor(format) {
|
|
57
|
+
return MIME_TYPES[format];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A URL-safe base name for generated files, from the source file name.
|
|
61
|
+
*/
|
|
62
|
+
export function imageBaseName(filePath) {
|
|
63
|
+
return slugify(basename(filePath, extname(filePath))) || 'image';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Plans the copy of a co-located image: `/_img/{name}-{hash}.{ext}`.
|
|
67
|
+
*
|
|
68
|
+
* @param filePath - Absolute source path
|
|
69
|
+
* @param sourceHash - SHA-256 of the source bytes
|
|
70
|
+
* @param format - Source format
|
|
71
|
+
*/
|
|
72
|
+
export function planCopy(filePath, sourceHash, format) {
|
|
73
|
+
const relPath = posix.join(IMAGES_OUTPUT_DIR, `${imageBaseName(filePath)}-${sourceHash.slice(0, NAME_HASH_LENGTH)}.${extensionFor(format)}`);
|
|
74
|
+
return { key: `copy:${relPath}`, relPath, url: `/${relPath}` };
|
|
75
|
+
}
|
|
76
|
+
function qualityFor(format, responsive) {
|
|
77
|
+
return responsive.quality[format];
|
|
78
|
+
}
|
|
79
|
+
function srcsetOf(outputs) {
|
|
80
|
+
return outputs.map((output) => `${output.url} ${output.width}w`).join(', ');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Plans the responsive variants of one image: `/_img/{name}-{hash}-{width}.{ext}` for every
|
|
84
|
+
* width in each modern format, plus the fallback format.
|
|
85
|
+
*/
|
|
86
|
+
export function planVariants(input) {
|
|
87
|
+
const widths = planWidths(input.width, input.responsive.widths);
|
|
88
|
+
const fallbackFormat = fallbackFormatFor(input.format);
|
|
89
|
+
const formats = input.responsive.formats.filter((format) => format !== fallbackFormat);
|
|
90
|
+
const hash = createHash('sha256')
|
|
91
|
+
.update(JSON.stringify({
|
|
92
|
+
source: input.sourceHash,
|
|
93
|
+
widths,
|
|
94
|
+
formats,
|
|
95
|
+
fallbackFormat,
|
|
96
|
+
quality: [...formats, fallbackFormat].map((f) => qualityFor(f, input.responsive) ?? null),
|
|
97
|
+
provider: input.providerVersion,
|
|
98
|
+
}))
|
|
99
|
+
.digest('hex');
|
|
100
|
+
const baseName = imageBaseName(input.filePath);
|
|
101
|
+
const name = `${baseName}-${hash.slice(0, NAME_HASH_LENGTH)}`;
|
|
102
|
+
const outputsFor = (format) => widths.map((width) => {
|
|
103
|
+
const relPath = posix.join(IMAGES_OUTPUT_DIR, `${name}-${width}.${extensionFor(format)}`);
|
|
104
|
+
const quality = qualityFor(format, input.responsive);
|
|
105
|
+
return {
|
|
106
|
+
relPath,
|
|
107
|
+
url: `/${relPath}`,
|
|
108
|
+
width,
|
|
109
|
+
format,
|
|
110
|
+
...(quality !== undefined && { quality }),
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
const sourceOutputs = formats.map((format) => ({ format, outputs: outputsFor(format) }));
|
|
114
|
+
const fallbackOutputs = outputsFor(fallbackFormat);
|
|
115
|
+
return {
|
|
116
|
+
// The name is part of the key: identical files under different names get their own outputs
|
|
117
|
+
key: `variants:${baseName}:${hash}`,
|
|
118
|
+
outputs: [...sourceOutputs.flatMap((group) => group.outputs), ...fallbackOutputs],
|
|
119
|
+
sources: sourceOutputs.map((group) => ({
|
|
120
|
+
type: mimeTypeFor(group.format),
|
|
121
|
+
srcset: srcsetOf(group.outputs),
|
|
122
|
+
})),
|
|
123
|
+
fallback: {
|
|
124
|
+
src: fallbackOutputs[fallbackOutputs.length - 1].url,
|
|
125
|
+
srcset: srcsetOf(fallbackOutputs),
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds and loads the optional `@stati/images` package.
|
|
3
|
+
* @module images/provider
|
|
4
|
+
*/
|
|
5
|
+
import type { ImageProvider } from '../types/index.js';
|
|
6
|
+
/** Provider contract version this core supports. */
|
|
7
|
+
export declare const SUPPORTED_PROVIDER_API_VERSION = 1;
|
|
8
|
+
/** Image features that need the package. */
|
|
9
|
+
export type ImageFeature = 'responsive' | 'og';
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the package from the project, as markdown-it plugins are, so it is found under
|
|
12
|
+
* strict layouts such as pnpm's where core cannot see the project's dependencies.
|
|
13
|
+
* Resolution walks up from the project, which also covers workspaces.
|
|
14
|
+
*
|
|
15
|
+
* @returns The resolved entry file, or null when the package is not installed
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveImagesPackage(cwd?: string): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* The error shown when a feature is on but the package is missing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function missingPackageMessage(features: ImageFeature[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* Checks that the package is installed and, when `load` is set, creates its provider.
|
|
24
|
+
*
|
|
25
|
+
* @param options.features - The features that are on; they decide the error message
|
|
26
|
+
* @param options.load - Whether to import the package; false only checks it is installed
|
|
27
|
+
* @returns The provider, or null when `load` is false
|
|
28
|
+
* @throws {Error} When the package is missing, fails to load, or is incompatible
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadImageProvider(options: {
|
|
31
|
+
features: ImageFeature[];
|
|
32
|
+
load: boolean;
|
|
33
|
+
cwd?: string;
|
|
34
|
+
}): Promise<ImageProvider | null>;
|
|
35
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/images/provider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,oDAAoD;AACpD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAEhD,4CAA4C;AAC5C,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,IAAI,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAO/E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAItE;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAuChC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds and loads the optional `@stati/images` package.
|
|
3
|
+
* @module images/provider
|
|
4
|
+
*/
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { resolve as resolvePath } from 'node:path';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { IMAGES_PACKAGE_NAME } from '../constants.js';
|
|
9
|
+
import { getStatiVersion } from '../core/utils/index.js';
|
|
10
|
+
/** Provider contract version this core supports. */
|
|
11
|
+
export const SUPPORTED_PROVIDER_API_VERSION = 1;
|
|
12
|
+
/**
|
|
13
|
+
* Resolves the package from the project, as markdown-it plugins are, so it is found under
|
|
14
|
+
* strict layouts such as pnpm's where core cannot see the project's dependencies.
|
|
15
|
+
* Resolution walks up from the project, which also covers workspaces.
|
|
16
|
+
*
|
|
17
|
+
* @returns The resolved entry file, or null when the package is not installed
|
|
18
|
+
*/
|
|
19
|
+
export function resolveImagesPackage(cwd = process.cwd()) {
|
|
20
|
+
try {
|
|
21
|
+
const require = createRequire(pathToFileURL(resolvePath(cwd, 'package.json')));
|
|
22
|
+
return require.resolve(IMAGES_PACKAGE_NAME);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The error shown when a feature is on but the package is missing.
|
|
30
|
+
*/
|
|
31
|
+
export function missingPackageMessage(features) {
|
|
32
|
+
const names = features.map((feature) => `images.${feature}`).join(' and ');
|
|
33
|
+
const verb = features.length > 1 ? 'require' : 'requires';
|
|
34
|
+
return `${names} ${verb} ${IMAGES_PACKAGE_NAME}. Run: npm i -D ${IMAGES_PACKAGE_NAME}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Checks that the package is installed and, when `load` is set, creates its provider.
|
|
38
|
+
*
|
|
39
|
+
* @param options.features - The features that are on; they decide the error message
|
|
40
|
+
* @param options.load - Whether to import the package; false only checks it is installed
|
|
41
|
+
* @returns The provider, or null when `load` is false
|
|
42
|
+
* @throws {Error} When the package is missing, fails to load, or is incompatible
|
|
43
|
+
*/
|
|
44
|
+
export async function loadImageProvider(options) {
|
|
45
|
+
const entry = resolveImagesPackage(options.cwd);
|
|
46
|
+
if (!entry) {
|
|
47
|
+
throw new Error(missingPackageMessage(options.features));
|
|
48
|
+
}
|
|
49
|
+
if (!options.load) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
let provider;
|
|
53
|
+
try {
|
|
54
|
+
const module = (await import(pathToFileURL(entry).href));
|
|
55
|
+
if (typeof module.createImageProvider !== 'function') {
|
|
56
|
+
throw new Error(`${IMAGES_PACKAGE_NAME} does not export createImageProvider()`);
|
|
57
|
+
}
|
|
58
|
+
provider = await module.createImageProvider();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw new Error(`Failed to load ${IMAGES_PACKAGE_NAME}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
62
|
+
}
|
|
63
|
+
if (provider?.apiVersion !== SUPPORTED_PROVIDER_API_VERSION) {
|
|
64
|
+
throw new Error(`${IMAGES_PACKAGE_NAME} ${provider?.version ?? ''} is not compatible with @stati/core ${getStatiVersion()}. Update both packages.`.replace(/\s+/g, ' '));
|
|
65
|
+
}
|
|
66
|
+
if (options.features.includes('og') && typeof provider.renderOgImage !== 'function') {
|
|
67
|
+
throw new Error(`images.og needs a newer ${IMAGES_PACKAGE_NAME} (${provider.version}). Update the package.`);
|
|
68
|
+
}
|
|
69
|
+
return provider;
|
|
70
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps an image `src` as written in Markdown or a template to a file on disk.
|
|
3
|
+
* @module images/resolve
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Where an image's `src` points.
|
|
7
|
+
* - `skip`: leave the image alone (remote, SVG, unsupported, opted out or outside the site);
|
|
8
|
+
* `src` is the value to emit, with any `?raw` suffix removed.
|
|
9
|
+
* - `static`: a file under the static directory, served at its own URL.
|
|
10
|
+
* - `colocated`: a file under the source directory, next to the page that uses it.
|
|
11
|
+
*/
|
|
12
|
+
export type ResolvedImageSource = {
|
|
13
|
+
kind: 'skip';
|
|
14
|
+
src: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'static';
|
|
17
|
+
src: string;
|
|
18
|
+
filePath: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'colocated';
|
|
21
|
+
src: string;
|
|
22
|
+
filePath: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Paths needed to resolve an image for one page.
|
|
26
|
+
*/
|
|
27
|
+
export interface ImageResolveContext {
|
|
28
|
+
/** Absolute path of the Markdown file being rendered */
|
|
29
|
+
pageSourcePath: string;
|
|
30
|
+
/** Absolute source directory */
|
|
31
|
+
srcDir: string;
|
|
32
|
+
/** Absolute static assets directory */
|
|
33
|
+
staticDir: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves an image `src`.
|
|
37
|
+
* Root-relative paths (`/images/hero.jpg`) resolve from the static directory; relative
|
|
38
|
+
* paths (`./hero.jpg`) resolve next to the page's Markdown file.
|
|
39
|
+
*
|
|
40
|
+
* @param rawSrc - The `src` as authored
|
|
41
|
+
* @param context - The page and site directories
|
|
42
|
+
* @returns Where the image lives, or `skip` when it should be left as authored
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveImageSource(rawSrc: string, context: ImageResolveContext): ResolvedImageSource;
|
|
45
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/images/resolve.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,mBAAmB,GAC3B,mBAAmB,CAmCrB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps an image `src` as written in Markdown or a template to a file on disk.
|
|
3
|
+
* @module images/resolve
|
|
4
|
+
*/
|
|
5
|
+
import { dirname, extname, join, resolve } from 'node:path';
|
|
6
|
+
import { isPathWithinDirectory } from '../core/utils/index.js';
|
|
7
|
+
/** Extensions Stati reads dimensions from. SVG and anything else are left as authored. */
|
|
8
|
+
const SUPPORTED_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.avif']);
|
|
9
|
+
/** Suffix that opts a single image out of processing. */
|
|
10
|
+
const RAW_SUFFIX = '?raw';
|
|
11
|
+
/**
|
|
12
|
+
* Resolves an image `src`.
|
|
13
|
+
* Root-relative paths (`/images/hero.jpg`) resolve from the static directory; relative
|
|
14
|
+
* paths (`./hero.jpg`) resolve next to the page's Markdown file.
|
|
15
|
+
*
|
|
16
|
+
* @param rawSrc - The `src` as authored
|
|
17
|
+
* @param context - The page and site directories
|
|
18
|
+
* @returns Where the image lives, or `skip` when it should be left as authored
|
|
19
|
+
*/
|
|
20
|
+
export function resolveImageSource(rawSrc, context) {
|
|
21
|
+
const src = rawSrc.trim();
|
|
22
|
+
if (!src)
|
|
23
|
+
return { kind: 'skip', src: rawSrc };
|
|
24
|
+
if (src.endsWith(RAW_SUFFIX)) {
|
|
25
|
+
return { kind: 'skip', src: src.slice(0, -RAW_SUFFIX.length) };
|
|
26
|
+
}
|
|
27
|
+
// Remote URLs, protocol-relative URLs and data URIs
|
|
28
|
+
if (/^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(src)) {
|
|
29
|
+
return { kind: 'skip', src };
|
|
30
|
+
}
|
|
31
|
+
let path;
|
|
32
|
+
try {
|
|
33
|
+
path = decodeURIComponent(src.replace(/[?#].*$/, ''));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { kind: 'skip', src };
|
|
37
|
+
}
|
|
38
|
+
if (!SUPPORTED_EXTENSIONS.has(extname(path).toLowerCase())) {
|
|
39
|
+
return { kind: 'skip', src };
|
|
40
|
+
}
|
|
41
|
+
if (path.startsWith('/')) {
|
|
42
|
+
const filePath = join(context.staticDir, path);
|
|
43
|
+
return isPathWithinDirectory(context.staticDir, filePath)
|
|
44
|
+
? { kind: 'static', src, filePath }
|
|
45
|
+
: { kind: 'skip', src };
|
|
46
|
+
}
|
|
47
|
+
const filePath = resolve(dirname(context.pageSourcePath), path);
|
|
48
|
+
return isPathWithinDirectory(context.srcDir, filePath)
|
|
49
|
+
? { kind: 'colocated', src, filePath }
|
|
50
|
+
: { kind: 'skip', src };
|
|
51
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-build image pipeline: renders image markup while pages render, runs the jobs the
|
|
3
|
+
* written pages need, recovers from failed jobs and prunes outputs no page uses.
|
|
4
|
+
* @module images/runtime
|
|
5
|
+
*/
|
|
6
|
+
import type { Eta } from 'eta';
|
|
7
|
+
import type Token from 'markdown-it/lib/token.mjs';
|
|
8
|
+
import type { CacheManifest, GeneratedOgImage, ImageProvider, Logger, OgFont, PageModel, StatiConfig } from '../types/index.js';
|
|
9
|
+
import type { RenderedAssets } from '../core/isg/builder.js';
|
|
10
|
+
import { type ResolvedImagesConfig, type ResolvedOgConfig, type ResolvedResponsiveConfig } from './config.js';
|
|
11
|
+
import { type SourceInfo } from './sources.js';
|
|
12
|
+
import { type CopyPlan, type VariantPlan } from './plan.js';
|
|
13
|
+
/**
|
|
14
|
+
* Options for `stati.image()` in templates.
|
|
15
|
+
*/
|
|
16
|
+
export interface StatiImageOptions {
|
|
17
|
+
/** Alternative text; use an empty string for decorative images */
|
|
18
|
+
alt?: string;
|
|
19
|
+
/** `sizes` attribute for responsive images */
|
|
20
|
+
sizes?: string;
|
|
21
|
+
/** Load eagerly with high fetch priority, for the main image above the fold */
|
|
22
|
+
priority?: boolean;
|
|
23
|
+
/** `class` attribute */
|
|
24
|
+
class?: string;
|
|
25
|
+
/** `title` attribute */
|
|
26
|
+
title?: string;
|
|
27
|
+
}
|
|
28
|
+
/** A unit of work that writes one or more files under the output directory. */
|
|
29
|
+
interface JobSpec {
|
|
30
|
+
key: string;
|
|
31
|
+
/** Output files, relative to the output directory */
|
|
32
|
+
outputs: string[];
|
|
33
|
+
/** Source or page, for messages */
|
|
34
|
+
label: string;
|
|
35
|
+
/** Absolute path of the source image, for jobs that copy or encode one */
|
|
36
|
+
sourcePath?: string;
|
|
37
|
+
run: () => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/** One processed image (or Open Graph card) in a page. */
|
|
40
|
+
interface ImageUse {
|
|
41
|
+
jobKey: string;
|
|
42
|
+
outputs: string[];
|
|
43
|
+
/** A URL the written page contains when it actually uses this image */
|
|
44
|
+
marker: string;
|
|
45
|
+
/** Markup core emitted, swapped for `fallbackMarkup` if the job fails */
|
|
46
|
+
markup?: string;
|
|
47
|
+
fallbackMarkup?: string;
|
|
48
|
+
/** Copy the fallback markup needs, for co-located sources */
|
|
49
|
+
fallbackCopy?: JobSpec;
|
|
50
|
+
/** Set for Open Graph cards: their tags are removed if the job fails */
|
|
51
|
+
ogImage?: GeneratedOgImage;
|
|
52
|
+
}
|
|
53
|
+
/** Fonts and template for Open Graph cards, loaded once per build. */
|
|
54
|
+
interface OgState {
|
|
55
|
+
fonts: OgFont[];
|
|
56
|
+
fontsHash: string;
|
|
57
|
+
defaultTemplate: string | undefined;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Failures found while settling jobs.
|
|
61
|
+
*/
|
|
62
|
+
export interface SettleResult {
|
|
63
|
+
/** Pages rewritten to fall back to plain images */
|
|
64
|
+
patchedPages: number;
|
|
65
|
+
/** Failed jobs, for reporting */
|
|
66
|
+
failures: Array<{
|
|
67
|
+
label: string;
|
|
68
|
+
message: string;
|
|
69
|
+
og: boolean;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Checks that og:image URLs will be absolute and public.
|
|
74
|
+
* @throws {Error} When `site.baseUrl` is not an absolute http(s) URL on a public host
|
|
75
|
+
*/
|
|
76
|
+
export declare function validateOgBaseUrl(baseUrl: string): void;
|
|
77
|
+
/**
|
|
78
|
+
* Image state for one page while it renders.
|
|
79
|
+
*/
|
|
80
|
+
export declare class PageImages {
|
|
81
|
+
private readonly runtime;
|
|
82
|
+
readonly page: PageModel;
|
|
83
|
+
/** Source files whose content affects the page */
|
|
84
|
+
readonly assetDeps: Set<string>;
|
|
85
|
+
/** Processed images and cards, filtered against the written HTML when the page commits */
|
|
86
|
+
readonly uses: ImageUse[];
|
|
87
|
+
/** The Markdown image that `eagerFirst` loads eagerly, chosen by `renderMarkdown()` */
|
|
88
|
+
eagerToken: Token | undefined;
|
|
89
|
+
private readonly context;
|
|
90
|
+
constructor(runtime: ImageRuntime, page: PageModel);
|
|
91
|
+
/**
|
|
92
|
+
* Renders a Markdown image token.
|
|
93
|
+
*
|
|
94
|
+
* @returns The markup, or the `src` to give markdown-it's default renderer when the image
|
|
95
|
+
* is left as authored
|
|
96
|
+
*/
|
|
97
|
+
renderMarkdownImage(token: Token, alt: string): {
|
|
98
|
+
html: string;
|
|
99
|
+
} | {
|
|
100
|
+
src: string;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Implements `stati.image(src, options)` for templates. Synchronous, because templates
|
|
104
|
+
* print its result directly.
|
|
105
|
+
*/
|
|
106
|
+
renderHelperImage(src: unknown, options?: StatiImageOptions): string;
|
|
107
|
+
private renderImage;
|
|
108
|
+
/**
|
|
109
|
+
* Whether this page gets a generated Open Graph image.
|
|
110
|
+
*/
|
|
111
|
+
wantsOg(): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Size of the page's Open Graph image, exposed to the template as `stati.og`.
|
|
114
|
+
*/
|
|
115
|
+
ogSize(): {
|
|
116
|
+
width: number;
|
|
117
|
+
height: number;
|
|
118
|
+
} | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* Renders this page's Open Graph template and registers the card.
|
|
121
|
+
*
|
|
122
|
+
* @param context - Template context: the layout's, with an empty body and `stati.og`
|
|
123
|
+
* @param eta - The template engine
|
|
124
|
+
* @returns The generated image, or null when the page gets none
|
|
125
|
+
*/
|
|
126
|
+
renderOg(context: object, eta: Eta): Promise<GeneratedOgImage | null>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Options for {@link ImageRuntime.create}.
|
|
130
|
+
*/
|
|
131
|
+
export interface ImageRuntimeOptions {
|
|
132
|
+
/** Absolute output directory */
|
|
133
|
+
outDir: string;
|
|
134
|
+
/** Absolute cache directory (`.stati`) */
|
|
135
|
+
cacheDir: string;
|
|
136
|
+
logger: Logger;
|
|
137
|
+
/** True for dev server builds */
|
|
138
|
+
development: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The image pipeline for one build.
|
|
142
|
+
*/
|
|
143
|
+
export declare class ImageRuntime {
|
|
144
|
+
readonly settings: ResolvedImagesConfig;
|
|
145
|
+
readonly provider: ImageProvider | null;
|
|
146
|
+
readonly ogState: OgState | null;
|
|
147
|
+
private readonly manifest;
|
|
148
|
+
private readonly existing;
|
|
149
|
+
readonly srcDir: string;
|
|
150
|
+
readonly staticDir: string;
|
|
151
|
+
readonly outDir: string;
|
|
152
|
+
private readonly cacheDir;
|
|
153
|
+
readonly baseUrl: string;
|
|
154
|
+
readonly logger: Logger;
|
|
155
|
+
readonly development: boolean;
|
|
156
|
+
/** Responsive options in effect for this build; null when off or skipped in dev */
|
|
157
|
+
readonly responsive: ResolvedResponsiveConfig | null;
|
|
158
|
+
/** Open Graph options in effect for this build; null when off or skipped in dev */
|
|
159
|
+
readonly og: ResolvedOgConfig | null;
|
|
160
|
+
/** Counters for the build summary and metrics */
|
|
161
|
+
readonly stats: {
|
|
162
|
+
generated: number;
|
|
163
|
+
cached: number;
|
|
164
|
+
failed: number;
|
|
165
|
+
};
|
|
166
|
+
private readonly specs;
|
|
167
|
+
/** Source file of each generated image registered this build, by URL path */
|
|
168
|
+
private readonly generatedSources;
|
|
169
|
+
private readonly jobs;
|
|
170
|
+
private readonly failures;
|
|
171
|
+
private readonly records;
|
|
172
|
+
private readonly touched;
|
|
173
|
+
private readonly warned;
|
|
174
|
+
/** Whether the images manifest changed and needs saving */
|
|
175
|
+
private dirty;
|
|
176
|
+
private constructor();
|
|
177
|
+
/**
|
|
178
|
+
* Creates the pipeline for a build.
|
|
179
|
+
*
|
|
180
|
+
* @returns The runtime, or null when images are turned off with `images: false`
|
|
181
|
+
* @throws {ConfigValidationError} When the `images` block is invalid
|
|
182
|
+
* @throws {Error} When a feature needs `@stati/images` and it is missing or incompatible,
|
|
183
|
+
* or when an Open Graph font or `site.baseUrl` is unusable
|
|
184
|
+
*/
|
|
185
|
+
static create(config: StatiConfig, options: ImageRuntimeOptions): Promise<ImageRuntime | null>;
|
|
186
|
+
/**
|
|
187
|
+
* Settings that change generated HTML, added to every page's render-inputs hash.
|
|
188
|
+
*/
|
|
189
|
+
fingerprint(): Record<string, unknown>;
|
|
190
|
+
/**
|
|
191
|
+
* Creates the image state for a page about to render.
|
|
192
|
+
*/
|
|
193
|
+
createPageContext(page: PageModel): PageImages;
|
|
194
|
+
/**
|
|
195
|
+
* Whether every generated file a cached page references still exists.
|
|
196
|
+
*/
|
|
197
|
+
outputsExist(images: readonly string[] | undefined): boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Records a written page and starts the jobs for the images its HTML actually contains.
|
|
200
|
+
*
|
|
201
|
+
* @param pageImages - The page's image state
|
|
202
|
+
* @param html - The final HTML of the page
|
|
203
|
+
* @param info - Output path and cache key of the page
|
|
204
|
+
* @returns The files to record on the page's cache entry
|
|
205
|
+
*/
|
|
206
|
+
commitPage(pageImages: PageImages, html: string, info: {
|
|
207
|
+
outputPath: string;
|
|
208
|
+
cacheKey: string;
|
|
209
|
+
}): RenderedAssets;
|
|
210
|
+
/**
|
|
211
|
+
* Waits for every job, then rewrites pages whose images failed so they show plain images,
|
|
212
|
+
* and marks those pages stale so the next build retries.
|
|
213
|
+
*
|
|
214
|
+
* @param cacheManifest - The ISG manifest, updated in place
|
|
215
|
+
*/
|
|
216
|
+
settle(cacheManifest: CacheManifest): Promise<SettleResult>;
|
|
217
|
+
/**
|
|
218
|
+
* Saves the images manifest.
|
|
219
|
+
*/
|
|
220
|
+
save(): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Deletes generated files no page of this build references. Only files recorded in the
|
|
223
|
+
* images manifest are ever deleted.
|
|
224
|
+
*
|
|
225
|
+
* @param liveImages - Generated files referenced by this build's pages
|
|
226
|
+
* @param liveSources - Source files referenced by this build's pages
|
|
227
|
+
* @returns Number of files deleted
|
|
228
|
+
*/
|
|
229
|
+
prune(liveImages: ReadonlySet<string>, liveSources: ReadonlySet<string>): Promise<number>;
|
|
230
|
+
/**
|
|
231
|
+
* Inspects a source image and remembers it for the manifest.
|
|
232
|
+
*/
|
|
233
|
+
inspect(filePath: string): SourceInfo | null;
|
|
234
|
+
/**
|
|
235
|
+
* Logs a warning once per build.
|
|
236
|
+
*/
|
|
237
|
+
warnOnce(key: string, message: string): void;
|
|
238
|
+
/**
|
|
239
|
+
* Registers a job. It only runs once a written page references its outputs.
|
|
240
|
+
*/
|
|
241
|
+
registerJob(spec: JobSpec): void;
|
|
242
|
+
/**
|
|
243
|
+
* The source file of an image this build generates, by its URL path, such as
|
|
244
|
+
* `/_img/hero-1a2b3c4d-960.webp`.
|
|
245
|
+
*/
|
|
246
|
+
generatedSourceFor(path: string): string | undefined;
|
|
247
|
+
/**
|
|
248
|
+
* Job that copies a co-located image to its hashed URL.
|
|
249
|
+
*/
|
|
250
|
+
copyJob(plan: CopyPlan, sourcePath: string): JobSpec;
|
|
251
|
+
/**
|
|
252
|
+
* Job that encodes the responsive variants of an image.
|
|
253
|
+
*/
|
|
254
|
+
variantJob(plan: VariantPlan, sourcePath: string): JobSpec;
|
|
255
|
+
private startJob;
|
|
256
|
+
}
|
|
257
|
+
export {};
|
|
258
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/images/runtime.ts"],"names":[],"mappings":"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"}
|