Skip to content

Commit 3fcf81d

Browse files
authored
feat: Added local image support
Merge pull request #6 from uploadcare/local-images-support
2 parents e9e1ba5 + 4decc23 commit 3fcf81d

File tree

7 files changed

+102
-27
lines changed

7 files changed

+102
-27
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ Alternatively, in case you're using a custom proxy, set the proxy domain.
5858
NEXT_PUBLIC_UPLOADCARE_CUSTOM_PROXY_DOMAIN="proxy.example.com"
5959
```
6060

61+
Then set the app base path (to get relative image urls processed in Production):
62+
63+
```ini
64+
#.env
65+
NEXT_PUBLIC_UPLOADCARE_APP_BASE_PATH="https://example.com/"
66+
```
67+
6168
That's it. You may now use `@uploadcare/nextjs-loader` in your app (see [Usage](#usage)).
6269

6370
---

example/pages/_app.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import '../styles/globals.css'
21
import type { AppProps } from 'next/app'
2+
import { ReactElement } from 'react'
3+
import '../styles/globals.css'
34

4-
function MyApp({ Component, pageProps }: AppProps) {
5+
function MyApp({ Component, pageProps }: AppProps): ReactElement {
56
return <Component {...pageProps} />
67
}
78
export default MyApp

example/pages/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ const Home: NextPage = () => (
6767
width={64}
6868
height={64}
6969
loader={uploadcareLoader}
70+
/>
71+
<hr className={styles.hr} />
72+
<p>Local image will be served AS IS in Development, and converted to the absolute URL and passed to the proxy in Production</p>
73+
<Image
74+
alt="A local image"
75+
src="/local_image.png"
76+
width={494}
77+
height={332}
78+
loader={uploadcareLoader}
7079
/>
7180
<hr className={styles.hr} />
7281
Checkout the documentation for{' '}

example/public/local_image.png

4.88 KB
Loading

example/yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@
284284
"@typescript-eslint/types" "4.33.0"
285285
eslint-visitor-keys "^2.0.0"
286286

287+
"@uploadcare/nextjs-loader@*":
288+
version "0.1.0"
289+
resolved "https://registry.yarnpkg.com/@uploadcare/nextjs-loader/-/nextjs-loader-0.1.0.tgz#e922dfd2275a63890a09c2d3190ce385e6e28426"
290+
integrity sha512-cGomYrxKdMl+JCPIe0E6Pb0vS+2RHon4+ezBO1dLsIvzzkgMKvZEVC8fplWg1j7pzUpxvgAnAMNN+Pri3XhaRQ==
291+
287292
acorn-jsx@^5.3.1:
288293
version "5.3.2"
289294
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
@@ -3144,11 +3149,6 @@ unpipe@1.0.0:
31443149
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
31453150
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
31463151

3147-
uploadcare-nextjs-loader1@*:
3148-
version "0.1.0"
3149-
resolved "https://registry.yarnpkg.com/uploadcare-nextjs-loader1/-/uploadcare-nextjs-loader1-0.1.0.tgz#9c28a377ff76d06c5773ec5beb01d7e5f09ddeed"
3150-
integrity sha512-zU9pXyj2O8krjrgULAvuk/T6PaED0tUULS6OvEJsA11jbSjU5PSNhc3RRZJYkfr8bHWQ6JK1RsdYA9OAAQlkLQ==
3151-
31523152
uri-js@^4.2.2:
31533153
version "4.4.1"
31543154
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"

src/__tests__/uploadcare-loader.spec.ts

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,54 @@ describe('uploadcareLoader', () => {
77
cleanup();
88
});
99

10-
test("The loader validates the 'src' parameter", () => {
10+
test("The loader returns relative 'src' AS IS in Development environment", () => {
1111
addEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY', 'test-public-key');
1212

1313
const src = '/relative/image.jpg';
1414

15-
const t = () => {
16-
uploadcareLoader({
17-
src,
18-
width: 0,
19-
quality: 80
20-
});
21-
};
15+
const result = uploadcareLoader({
16+
src,
17+
width: 0,
18+
quality: 80
19+
});
2220

23-
expect(t).toThrow(
24-
`Failed to parse "${src}" in "uploadcareLoader", Uploadcare loader doesn't support relative images.`
25-
);
21+
expect(result).toEqual(src);
22+
23+
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY');
24+
});
25+
26+
test("The loader builds up a relative 'src' to the absolute URL and passes it to the proxy in Production environment", () => {
27+
addEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY', 'test-public-key');
28+
addEnvVar('NODE_ENV', 'production');
29+
30+
// When the base path is not set through env vars.
31+
32+
const src = '/relative/image.jpg';
33+
34+
let result = uploadcareLoader({
35+
src,
36+
width: 0,
37+
quality: 80
38+
});
2639

40+
expect(result).toEqual(src);
41+
42+
// When the base path is set through env vars.
43+
44+
const basePath = 'https://example.com';
45+
addEnvVar('NEXT_PUBLIC_UPLOADCARE_APP_BASE_PATH', basePath);
46+
47+
result = uploadcareLoader({
48+
src,
49+
width: 0,
50+
quality: 80
51+
});
52+
53+
expect(result).toEqual(`${basePath}${src}`);
54+
55+
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_APP_BASE_PATH');
2756
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY');
57+
removeEnvVar('NODE_ENV');
2858
});
2959

3060
test('The loader requires either a public key or a custom proxy domain', () => {
@@ -117,7 +147,7 @@ describe('uploadcareLoader', () => {
117147
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_TRANSFORMATION_PARAMETERS');
118148
});
119149

120-
test("The loader doesn't process SVG and GIF", () => {
150+
test("The loader doesn't process SVG and GIF (absolute url)", () => {
121151
const src = 'https:/example.com/image.svg';
122152

123153
addEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY', 'test-public-key');
@@ -128,7 +158,23 @@ describe('uploadcareLoader', () => {
128158
quality: 80
129159
});
130160

131-
expect(result).toBe(`https://test-public-key.ucr.io${src}`);
161+
expect(result).toBe(src);
162+
163+
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY');
164+
});
165+
166+
test("The loader doesn't process SVG and GIF (relative url)", () => {
167+
const src = '/image.svg';
168+
169+
addEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY', 'test-public-key');
170+
171+
const result = uploadcareLoader({
172+
src,
173+
width: 500,
174+
quality: 80
175+
});
176+
177+
expect(result).toBe(src);
132178

133179
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY');
134180
});

src/utils/loader.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ export function uploadcareLoader({
3838
const proxyEndpoint =
3939
customProxyEndpoint || generateDefaultProxyEndpoint(publicKey);
4040

41-
const root = trimTrailingSlash(proxyEndpoint);
41+
const basePath = trimTrailingSlash(
42+
process.env.NEXT_PUBLIC_UPLOADCARE_APP_BASE_PATH || ''
43+
);
44+
45+
const proxy = trimTrailingSlash(proxyEndpoint);
4246

4347
const isOnCdn = isCdnUrl(src, cdnDomain);
4448

@@ -53,19 +57,27 @@ export function uploadcareLoader({
5357
}
5458

5559
if (src.startsWith('/')) {
56-
throw new Error(
57-
`Failed to parse "${src}" in "uploadcareLoader", Uploadcare loader doesn't support relative images.`
58-
);
60+
return src;
61+
}
62+
}
63+
64+
// Process local images in Production.
65+
if (isProduction() && !isOnCdn && src.startsWith('/')) {
66+
const isBasePathSet = !isDotenvParamEmpty(basePath);
67+
68+
if (!isBasePathSet) {
69+
return src;
5970
}
71+
72+
return `${basePath}${src}`;
6073
}
6174

6275
const filename = getFilename(src);
6376
const extension = getExtension(filename);
6477

6578
// Some extensions are not processed by Uploadcare, e.g. SVG.
6679
if (NOT_PROCESSED_EXTENSIONS.includes(extension)) {
67-
// @todo: Test non-CDN urls.
68-
return isOnCdn ? src : `${root}${src}`;
80+
return isOnCdn ? src : `${basePath}${src}`;
6981
}
7082

7183
// Demo: https://ucarecdn.com/a6f8abc8-f92e-460a-b7a1-c5cd70a18cdb/-/format/auto/-/resize/300x/vercel.png
@@ -94,5 +106,5 @@ export function uploadcareLoader({
94106
return `${withoutFilename}${apiParamsString}${filename}`;
95107
}
96108

97-
return `${root}${apiParamsString}${src}`;
109+
return `${proxy}${apiParamsString}${src}`;
98110
}

0 commit comments

Comments
 (0)