@froxz/vite-plugin-s3
Version:
Allows you to upload files to AWS S3 or any other S3 compatible provider
181 lines (174 loc) • 4.98 kB
JavaScript
// src/helpers.ts
import path from "path";
import { isRegExp, isString } from "es-toolkit";
import readDir from "recursive-readdir";
var S3_PATH_SEP = "/";
var PATH_SEP = path.sep;
var DEFAULT_UPLOAD_OPTIONS = {
ACL: "public-read"
};
var UPLOAD_IGNORES = [
".DS_Store"
];
function addTrailingS3Sep(fPath) {
return fPath ? fPath.replace(/\/?(\?|#|$)/, "/$1") : fPath;
}
function addSeperatorToPath(fPath) {
if (!fPath)
return fPath;
return fPath.endsWith(PATH_SEP) ? fPath : fPath + PATH_SEP;
}
function translatePathFromFiles(dir, files) {
return files.map((file) => ({
path: file,
name: file.replace(dir, "").split(PATH_SEP).join(S3_PATH_SEP)
}));
}
function getDirectoryFilesRecursive(dir, ignores = []) {
return new Promise((resolve2, reject) => {
readDir(dir, ignores, (error, files) => {
if (error)
reject(error);
else
resolve2(translatePathFromFiles(dir, files));
});
});
}
function testRule(rule, subject) {
if (isRegExp(rule))
return rule.test(subject);
else if (typeof rule === "function")
return !!rule(subject);
else if (Array.isArray(rule))
return rule.every((condition) => testRule(condition, subject));
else if (isString(rule))
return new RegExp(rule).test(subject);
else
throw new Error("Invalid include / exclude rule");
}
// src/context.ts
function createContext(options) {
const basePath = options.basePath ? addTrailingS3Sep(options.basePath) : "";
return {
...options,
basePath
};
}
// src/uploader.ts
import fs from "fs";
import { resolve } from "path";
import { S3 } from "@aws-sdk/client-s3";
import { mapValues } from "es-toolkit";
import { lookup } from "mime-types";
// src/log.ts
import { relative } from "path";
import { cyan, dim } from "kolorist";
// package.json
var version = "2.0.5";
// src/log.ts
function logResult(files, vite) {
const { root, logLevel = "info" } = vite;
if (logLevel === "silent")
return;
if (logLevel === "info") {
console.info([
"",
`${cyan(`ViteS3 v${version}`)}`,
`\u2713 ${files.length} files uploaded`,
...files.map((p) => `${dim(relative(root, p.path))}`)
].join("\n"));
}
}
// src/uploader.ts
var Uploader = class {
options;
vite;
client;
directory;
constructor(options, vite) {
this.options = options;
this.vite = vite;
this.client = new S3(this.options.clientConfig);
this.directory = resolve(this.vite.root, this.options.directory ? this.options.directory : this.vite.build.outDir);
}
uploadFile(fileName, file) {
let Key = this.options.basePath + fileName;
const params = mapValues(this.options.uploadOptions, (optionConfig) => {
return typeof optionConfig === "function" ? optionConfig(fileName, file) : optionConfig;
});
if (Key[0] === "/")
Key = Key.substr(1);
if (params.ContentType === void 0)
params.ContentType = lookup(fileName) || "application/octet-stream";
const Body = fs.createReadStream(file);
return this.client.putObject({
Key,
Body,
...DEFAULT_UPLOAD_OPTIONS,
...params
});
}
async uploadFiles(files) {
if (this.options.sequentialUploads === true) {
const uploadResponses = [];
for (const file of files) {
const response = await this.uploadFile(file.name, file.path);
uploadResponses.push(response);
}
return uploadResponses;
}
const uploadFiles = files.map((file) => this.uploadFile(file.name, file.path));
return await Promise.all(uploadFiles);
}
isIgnoredFile(file) {
return UPLOAD_IGNORES.some((ignore) => new RegExp(ignore).test(file));
}
isIncludeAndNotExclude(file) {
const { include, exclude } = this.options;
const isInclude = include ? testRule(include, file) : true;
const isExclude = exclude ? testRule(exclude, file) : false;
return isInclude && !isExclude;
}
filterAllowedFiles(files) {
return files.reduce((res, file) => {
if (this.isIncludeAndNotExclude(file.name) && !this.isIgnoredFile(file.name))
res.push(file);
return res;
}, []);
}
async apply() {
const dPath = addSeperatorToPath(this.directory);
const allFiles = await getDirectoryFilesRecursive(dPath);
const files = this.filterAllowedFiles(allFiles);
await this.uploadFiles(files);
logResult(files, this.vite);
}
};
// src/index.ts
function ViteS3(enabled, userOptions) {
const options = createContext(userOptions);
let vite;
return {
name: "vite-plugin-s3",
enforce: "post",
apply(config, { command }) {
return command === "build" && enabled;
},
configResolved(config) {
vite = config;
},
closeBundle: {
async handler() {
if (!vite.build.ssr && enabled) {
const uploader = new Uploader(options, vite);
await uploader.apply();
}
}
}
};
}
export {
ViteS3 as S3Plugin,
ViteS3
};
//# sourceMappingURL=index.js.map