How to Use jsdelivr Free CDN to Automatically Upload and Replace Local Image Paths via Script

In production environments, storing images on a CDN (Content Delivery Network) can speed up loading times and improve user experience. Recently, I encountered a requirement to reference local images in a project, such as /images/xxx.{jpg,jpeg,png,gif}, for ease of local development. After development is completed, all images in the images directory need to be compressed and moved to /project/resources/images (resources is a git project), automatically uploaded to GitHub via git, and local project image paths need to be found and changed to CDN paths. Understanding the whole process, we can automate it with a script. First, let’s understand how jsdelivr implements CDN:

Relationship between jsdelivr CDN and GitHub

jsdelivr is a free and reliable CDN service that allows developers to store static resources in a GitHub repository and accelerate access globally via jsdelivr. Specifically, jsdelivr can provide a CDN address that accelerates resources stored in a GitHub repository, making these resources load quickly worldwide.

How to Associate a GitHub Project with jsdelivr

  1. Upload Resources to GitHub Repository
    First, developers need to upload static resources (such as images, JavaScript files, CSS files, etc.) to a specific directory in the GitHub repository. For example, you might have a GitHub repository named my-project with an images folder storing some images.

  2. Access via jsdelivr CDN
    Once resources are uploaded to the GitHub repository, you can access them via a URL provided by jsdelivr. jsdelivr provides a CDN address associated with the GitHub project, which follows this format:

    1
    https://cdn.jsdelivr.net/gh/[GitHubUsername]/[RepositoryName]@[TagOrBranch]/[FilePath]
    • [GitHubUsername]: Your GitHub username.
    • [RepositoryName]: The name of your GitHub repository.
    • [TagOrBranch]: The GitHub tag or branch you want to use (e.g., main or v1.0).
    • [FilePath]: The path to the resource in the repository.

    Example:

    Suppose your GitHub username is johnsmith, your repository name is my-project, and you have an images folder in the main branch with a file named logo.png. You can access this image via the following jsdelivr CDN URL:

    1
    https://cdn.jsdelivr.net/gh/johnsmith/my-project@main/images/logo.png

    On your web page, you can use this URL to reference the image, allowing jsdelivr to speed up loading from global server caches, improving load times.

  3. Automatic Synchronization
    When you update resources in the GitHub repository, jsdelivr will automatically pull the latest resources from GitHub, ensuring the CDN resources are up to date. This means that as long as you upload new versions of resources to the GitHub repository, jsdelivr’s URL will automatically update to provide the latest files.

Using this method, developers can leverage jsdelivr CDN’s global distribution network to accelerate the loading speed of static resources stored in GitHub repositories, enhancing user experience.

Implementation Steps

1. Install Dependencies

Terminal window
1
pnpm add sharp fs-extra globby simple-git replace-in-file;

2. Compress and Move Images

First, use the sharp library to compress images, retaining the folder structure, and move the images to the target directory. Here is the implementation code:

1
import { promises as fs } from "fs";
2
import path from "path";
3
import sharp from "sharp";
4
import fse from "fs-extra";
5
import { globby } from "globby";
6
7
const imagesDir = "/project/myproject/public/images";
8
const targetDir = "/project/resources/images";
9
10
async function compressAndCopyImages(srcDir, destDir) {
11
try {
12
const sourDir = `${srcDir}/**/*.{jpg,jpeg,png,gif}`;
13
await fse.ensureDir(destDir);
14
const entries = await globby([sourDir], { onlyFiles: false });
15
16
for (const entry of entries) {
17
const relativePath = path.relative(srcDir, entry);
18
const destPath = path.join(destDir, relativePath);
19
20
if ((await fs.stat(entry)).isDirectory()) {
21
await fse.ensureDir(destPath);
22
} else {
23
const metadata = await sharp(entry).metadata();
24
25
let options = {};
26
let formatOptions = {};
27
28
switch (metadata.format) {
29
case "gif":
30
options = { animated: true, limitInputPixels: false };
31
formatOptions = { colours: 128 };
32
break;
33
default:
34
formatOptions = { quality: 75 };
35
}
36
37
if (metadata.size < 10000) {
38
await fse.copy(entry, destPath);
39
console.log(`Copied ${relativePath} without compression`);
40
} else {
41
const dirPath = path.dirname(destPath);
42
await fse.ensureDir(dirPath);
43
await sharp(entry)?.[metadata.format](formatOptions).toFile(destPath);
44
console.log(`Compressed and copied ${relativePath}`);
45
}
46
}
47
}
48
} catch (error) {
49
console.error("Error during image compression and copy:", error);
50
}
51
}

3. Git Operations

Next, automatically upload the images to the GitHub repository via the simple-git library:

1
import simpleGit from "simple-git";
2
3
const gitRepoDir = "/project/resources";
4
5
async function gitOperations() {
6
try {
7
const git = simpleGit(gitRepoDir);
8
await git.add("./*");
9
await git.commit("Update images");
10
await git.push("origin", "main");
11
console.log("Pushed changes to GitHub");
12
} catch (error) {
13
console.error("Error during Git operations:", error);
14
}
15
}

4. Update Image Paths

Finally, use the replace-in-file library to replace the local image paths referenced in the project with the online paths:

1
import { globby } from "globby";
2
import { replaceInFile } from "replace-in-file";
3
4
// Directory and files where image paths need to be changed
5
const contentDir = "/project/myproject/src/content/**/*.{html,js,jsx,ts,tsx}";
6
const cdnBaseUrl =
7
"https://cdn.jsdelivr.net/gh/[GitHubUsername]/resources/images";
8
9
async function updateImagePaths() {
10
try {
11
const files = await globby([contentDir]);
12
13
const replaceOptions = {
14
files,
15
from: /(["'])\/images\/(.+\.(jpg|jpeg|png|gif))/g,
16
to: `$1${cdnBaseUrl}/$2`,
17
};
18
19
const results = await replaceInFile(replaceOptions);
20
console.log(
21
"Modified files:",
22
results
23
.filter((result) => result.hasChanged)
24
.map((result) => result.file),
25
);
26
} catch (error) {
27
console.error("Error during updating image paths:", error);
28
}
29
}

5. Main Function

Integrate the above steps into a main function:

1
(async function main() {
2
await compressAndCopyImages(imagesDir, targetDir);
3
await gitOperations();
4
await updateImagePaths();
5
})();

The above code compresses, moves, uploads, and replaces the paths of images, completing the automated process from local images to CDN hosting. This way, you can use local images during local development and CDN-accelerated images in production, improving website loading speed and performance.