-
Notifications
You must be signed in to change notification settings - Fork 24.8k
/
image_loader.ts
133 lines (118 loc) · 4.62 KB
/
image_loader.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken, Provider, ɵRuntimeError as RuntimeError} from '@angular/core';
import {RuntimeErrorCode} from '../../../errors';
import {isAbsoluteUrl, isValidPath, normalizePath, normalizeSrc} from '../url';
import {cloudinaryLoaderInfo} from './cloudinary_loader';
import {imageKitLoaderInfo} from './imagekit_loader';
import {imgixLoaderInfo} from './imgix_loader';
/**
* Config options recognized by the image loader function.
*
* @see `ImageLoader`
* @see `NgOptimizedImage`
* @publicApi
* @developerPreview
*/
export interface ImageLoaderConfig {
/**
* Image file name to be added to the image request URL.
*/
src: string;
/**
* Width of the requested image (to be used when generating srcset).
*/
width?: number;
}
/**
* Represents an image loader function. Image loader functions are used by the
* NgOptimizedImage directive to produce full image URL based on the image name and its width.
*
* @publicApi
* @developerPreview
*/
export type ImageLoader = (config: ImageLoaderConfig) => string;
/**
* Noop image loader that does no transformation to the original src and just returns it as is.
* This loader is used as a default one if more specific logic is not provided in an app config.
*
* @see `ImageLoader`
* @see `NgOptimizedImage`
*/
export const noopImageLoader = (config: ImageLoaderConfig) => config.src;
/**
* Metadata about the image loader.
*/
export type ImageLoaderInfo = {
name: string,
testUrl: (url: string) => boolean
};
/**
* Injection token that configures the image loader function.
*
* @see `ImageLoader`
* @see `NgOptimizedImage`
* @publicApi
* @developerPreview
*/
export const IMAGE_LOADER = new InjectionToken<ImageLoader>('ImageLoader', {
providedIn: 'root',
factory: () => noopImageLoader,
});
/**
* Internal helper function that makes it easier to introduce custom image loaders for the
* `NgOptimizedImage` directive. It is enough to specify a URL builder function to obtain full DI
* configuration for a given loader: a DI token corresponding to the actual loader function, plus DI
* tokens managing preconnect check functionality.
* @param buildUrlFn a function returning a full URL based on loader's configuration
* @param exampleUrls example of full URLs for a given loader (used in error messages)
* @returns a set of DI providers corresponding to the configured image loader
*/
export function createImageLoader(
buildUrlFn: (path: string, config: ImageLoaderConfig) => string, exampleUrls?: string[]) {
return function provideImageLoader(path: string) {
if (!isValidPath(path)) {
throwInvalidPathError(path, exampleUrls || []);
}
// The trailing / is stripped (if provided) to make URL construction (concatenation) easier in
// the individual loader functions.
path = normalizePath(path);
const loaderFn = (config: ImageLoaderConfig) => {
if (isAbsoluteUrl(config.src)) {
// Image loader functions expect an image file name (e.g. `my-image.png`)
// or a relative path + a file name (e.g. `/a/b/c/my-image.png`) as an input,
// so the final absolute URL can be constructed.
// When an absolute URL is provided instead - the loader can not
// build a final URL, thus the error is thrown to indicate that.
throwUnexpectedAbsoluteUrlError(path, config.src);
}
return buildUrlFn(path, {...config, src: normalizeSrc(config.src)});
};
const providers: Provider[] = [{provide: IMAGE_LOADER, useValue: loaderFn}];
return providers;
};
}
function throwInvalidPathError(path: unknown, exampleUrls: string[]): never {
throw new RuntimeError(
RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,
ngDevMode &&
`Image loader has detected an invalid path (\`${path}\`). ` +
`To fix this, supply a path using one of the following formats: ${
exampleUrls.join(' or ')}`);
}
function throwUnexpectedAbsoluteUrlError(path: string, url: string): never {
throw new RuntimeError(
RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,
ngDevMode &&
`Image loader has detected a \`<img>\` tag with an invalid \`ngSrc\` attribute: ${
url}. ` +
`This image loader expects \`ngSrc\` to be a relative URL - ` +
`however the provided value is an absolute URL. ` +
`To fix this, provide \`ngSrc\` as a path relative to the base URL ` +
`configured for this loader (\`${path}\`).`);
}