Files
2026-05-30 19:59:39 -03:00

1099 lines
36 KiB
JavaScript

import * as qs from "node:querystring";
import { createReadStream, existsSync, statSync } from "node:fs";
import path, { join, resolve, sep } from "node:path";
import { glob } from "tinyglobby";
import fs from "node:fs/promises";
import pc from "picocolors";
import { createHash } from "node:crypto";
import pMap from "p-map";
import chokidar from "chokidar";
//#region src/options.ts
const resolveOptions = (options) => ({
targets: options.targets,
structured: options.structured ?? false,
silent: options.silent ?? false,
watch: {
options: options.watch?.options ?? {},
reloadPageOnChange: options.watch?.reloadPageOnChange ?? false
},
hook: options.hook ?? "writeBundle",
environment: options.environment ?? "client"
});
//#endregion
//#region node_modules/.pnpm/@polka+url@1.0.0-next.29/node_modules/@polka/url/build.mjs
/**
* @typedef ParsedURL
* @type {import('.').ParsedURL}
*/
/**
* @typedef Request
* @property {string} url
* @property {ParsedURL} _parsedUrl
*/
/**
* @param {Request} req
* @returns {ParsedURL|void}
*/
function parse(req) {
let raw = req.url;
if (raw == null) return;
let prev = req._parsedUrl;
if (prev && prev.raw === raw) return prev;
let pathname = raw, search = "", query, hash;
if (raw.length > 1) {
let idx = raw.indexOf("#", 1);
if (idx !== -1) {
hash = raw.substring(idx);
pathname = raw.substring(0, idx);
}
idx = pathname.indexOf("?", 1);
if (idx !== -1) {
search = pathname.substring(idx);
pathname = pathname.substring(0, idx);
if (search.length > 1) query = qs.parse(search.substring(1));
}
}
return req._parsedUrl = {
pathname,
search,
query,
hash,
raw
};
}
//#endregion
//#region node_modules/.pnpm/mrmime@2.0.1/node_modules/mrmime/index.mjs
const mimes = {
"3g2": "video/3gpp2",
"3gp": "video/3gpp",
"3gpp": "video/3gpp",
"3mf": "model/3mf",
"aac": "audio/aac",
"ac": "application/pkix-attr-cert",
"adp": "audio/adpcm",
"adts": "audio/aac",
"ai": "application/postscript",
"aml": "application/automationml-aml+xml",
"amlx": "application/automationml-amlx+zip",
"amr": "audio/amr",
"apng": "image/apng",
"appcache": "text/cache-manifest",
"appinstaller": "application/appinstaller",
"appx": "application/appx",
"appxbundle": "application/appxbundle",
"asc": "application/pgp-keys",
"atom": "application/atom+xml",
"atomcat": "application/atomcat+xml",
"atomdeleted": "application/atomdeleted+xml",
"atomsvc": "application/atomsvc+xml",
"au": "audio/basic",
"avci": "image/avci",
"avcs": "image/avcs",
"avif": "image/avif",
"aw": "application/applixware",
"bdoc": "application/bdoc",
"bin": "application/octet-stream",
"bmp": "image/bmp",
"bpk": "application/octet-stream",
"btf": "image/prs.btif",
"btif": "image/prs.btif",
"buffer": "application/octet-stream",
"ccxml": "application/ccxml+xml",
"cdfx": "application/cdfx+xml",
"cdmia": "application/cdmi-capability",
"cdmic": "application/cdmi-container",
"cdmid": "application/cdmi-domain",
"cdmio": "application/cdmi-object",
"cdmiq": "application/cdmi-queue",
"cer": "application/pkix-cert",
"cgm": "image/cgm",
"cjs": "application/node",
"class": "application/java-vm",
"coffee": "text/coffeescript",
"conf": "text/plain",
"cpl": "application/cpl+xml",
"cpt": "application/mac-compactpro",
"crl": "application/pkix-crl",
"css": "text/css",
"csv": "text/csv",
"cu": "application/cu-seeme",
"cwl": "application/cwl",
"cww": "application/prs.cww",
"davmount": "application/davmount+xml",
"dbk": "application/docbook+xml",
"deb": "application/octet-stream",
"def": "text/plain",
"deploy": "application/octet-stream",
"dib": "image/bmp",
"disposition-notification": "message/disposition-notification",
"dist": "application/octet-stream",
"distz": "application/octet-stream",
"dll": "application/octet-stream",
"dmg": "application/octet-stream",
"dms": "application/octet-stream",
"doc": "application/msword",
"dot": "application/msword",
"dpx": "image/dpx",
"drle": "image/dicom-rle",
"dsc": "text/prs.lines.tag",
"dssc": "application/dssc+der",
"dtd": "application/xml-dtd",
"dump": "application/octet-stream",
"dwd": "application/atsc-dwd+xml",
"ear": "application/java-archive",
"ecma": "application/ecmascript",
"elc": "application/octet-stream",
"emf": "image/emf",
"eml": "message/rfc822",
"emma": "application/emma+xml",
"emotionml": "application/emotionml+xml",
"eps": "application/postscript",
"epub": "application/epub+zip",
"exe": "application/octet-stream",
"exi": "application/exi",
"exp": "application/express",
"exr": "image/aces",
"ez": "application/andrew-inset",
"fdf": "application/fdf",
"fdt": "application/fdt+xml",
"fits": "image/fits",
"g3": "image/g3fax",
"gbr": "application/rpki-ghostbusters",
"geojson": "application/geo+json",
"gif": "image/gif",
"glb": "model/gltf-binary",
"gltf": "model/gltf+json",
"gml": "application/gml+xml",
"gpx": "application/gpx+xml",
"gram": "application/srgs",
"grxml": "application/srgs+xml",
"gxf": "application/gxf",
"gz": "application/gzip",
"h261": "video/h261",
"h263": "video/h263",
"h264": "video/h264",
"heic": "image/heic",
"heics": "image/heic-sequence",
"heif": "image/heif",
"heifs": "image/heif-sequence",
"hej2": "image/hej2k",
"held": "application/atsc-held+xml",
"hjson": "application/hjson",
"hlp": "application/winhlp",
"hqx": "application/mac-binhex40",
"hsj2": "image/hsj2",
"htm": "text/html",
"html": "text/html",
"ics": "text/calendar",
"ief": "image/ief",
"ifb": "text/calendar",
"iges": "model/iges",
"igs": "model/iges",
"img": "application/octet-stream",
"in": "text/plain",
"ini": "text/plain",
"ink": "application/inkml+xml",
"inkml": "application/inkml+xml",
"ipfix": "application/ipfix",
"iso": "application/octet-stream",
"its": "application/its+xml",
"jade": "text/jade",
"jar": "application/java-archive",
"jhc": "image/jphc",
"jls": "image/jls",
"jp2": "image/jp2",
"jpe": "image/jpeg",
"jpeg": "image/jpeg",
"jpf": "image/jpx",
"jpg": "image/jpeg",
"jpg2": "image/jp2",
"jpgm": "image/jpm",
"jpgv": "video/jpeg",
"jph": "image/jph",
"jpm": "image/jpm",
"jpx": "image/jpx",
"js": "text/javascript",
"json": "application/json",
"json5": "application/json5",
"jsonld": "application/ld+json",
"jsonml": "application/jsonml+json",
"jsx": "text/jsx",
"jt": "model/jt",
"jxl": "image/jxl",
"jxr": "image/jxr",
"jxra": "image/jxra",
"jxrs": "image/jxrs",
"jxs": "image/jxs",
"jxsc": "image/jxsc",
"jxsi": "image/jxsi",
"jxss": "image/jxss",
"kar": "audio/midi",
"ktx": "image/ktx",
"ktx2": "image/ktx2",
"less": "text/less",
"lgr": "application/lgr+xml",
"list": "text/plain",
"litcoffee": "text/coffeescript",
"log": "text/plain",
"lostxml": "application/lost+xml",
"lrf": "application/octet-stream",
"m1v": "video/mpeg",
"m21": "application/mp21",
"m2a": "audio/mpeg",
"m2t": "video/mp2t",
"m2ts": "video/mp2t",
"m2v": "video/mpeg",
"m3a": "audio/mpeg",
"m4a": "audio/mp4",
"m4p": "application/mp4",
"m4s": "video/iso.segment",
"ma": "application/mathematica",
"mads": "application/mads+xml",
"maei": "application/mmt-aei+xml",
"man": "text/troff",
"manifest": "text/cache-manifest",
"map": "application/json",
"mar": "application/octet-stream",
"markdown": "text/markdown",
"mathml": "application/mathml+xml",
"mb": "application/mathematica",
"mbox": "application/mbox",
"md": "text/markdown",
"mdx": "text/mdx",
"me": "text/troff",
"mesh": "model/mesh",
"meta4": "application/metalink4+xml",
"metalink": "application/metalink+xml",
"mets": "application/mets+xml",
"mft": "application/rpki-manifest",
"mid": "audio/midi",
"midi": "audio/midi",
"mime": "message/rfc822",
"mj2": "video/mj2",
"mjp2": "video/mj2",
"mjs": "text/javascript",
"mml": "text/mathml",
"mods": "application/mods+xml",
"mov": "video/quicktime",
"mp2": "audio/mpeg",
"mp21": "application/mp21",
"mp2a": "audio/mpeg",
"mp3": "audio/mpeg",
"mp4": "video/mp4",
"mp4a": "audio/mp4",
"mp4s": "application/mp4",
"mp4v": "video/mp4",
"mpd": "application/dash+xml",
"mpe": "video/mpeg",
"mpeg": "video/mpeg",
"mpf": "application/media-policy-dataset+xml",
"mpg": "video/mpeg",
"mpg4": "video/mp4",
"mpga": "audio/mpeg",
"mpp": "application/dash-patch+xml",
"mrc": "application/marc",
"mrcx": "application/marcxml+xml",
"ms": "text/troff",
"mscml": "application/mediaservercontrol+xml",
"msh": "model/mesh",
"msi": "application/octet-stream",
"msix": "application/msix",
"msixbundle": "application/msixbundle",
"msm": "application/octet-stream",
"msp": "application/octet-stream",
"mtl": "model/mtl",
"mts": "video/mp2t",
"musd": "application/mmt-usd+xml",
"mxf": "application/mxf",
"mxmf": "audio/mobile-xmf",
"mxml": "application/xv+xml",
"n3": "text/n3",
"nb": "application/mathematica",
"nq": "application/n-quads",
"nt": "application/n-triples",
"obj": "model/obj",
"oda": "application/oda",
"oga": "audio/ogg",
"ogg": "audio/ogg",
"ogv": "video/ogg",
"ogx": "application/ogg",
"omdoc": "application/omdoc+xml",
"onepkg": "application/onenote",
"onetmp": "application/onenote",
"onetoc": "application/onenote",
"onetoc2": "application/onenote",
"opf": "application/oebps-package+xml",
"opus": "audio/ogg",
"otf": "font/otf",
"owl": "application/rdf+xml",
"oxps": "application/oxps",
"p10": "application/pkcs10",
"p7c": "application/pkcs7-mime",
"p7m": "application/pkcs7-mime",
"p7s": "application/pkcs7-signature",
"p8": "application/pkcs8",
"pdf": "application/pdf",
"pfr": "application/font-tdpfr",
"pgp": "application/pgp-encrypted",
"pkg": "application/octet-stream",
"pki": "application/pkixcmp",
"pkipath": "application/pkix-pkipath",
"pls": "application/pls+xml",
"png": "image/png",
"prc": "model/prc",
"prf": "application/pics-rules",
"provx": "application/provenance+xml",
"ps": "application/postscript",
"pskcxml": "application/pskc+xml",
"pti": "image/prs.pti",
"qt": "video/quicktime",
"raml": "application/raml+yaml",
"rapd": "application/route-apd+xml",
"rdf": "application/rdf+xml",
"relo": "application/p2p-overlay+xml",
"rif": "application/reginfo+xml",
"rl": "application/resource-lists+xml",
"rld": "application/resource-lists-diff+xml",
"rmi": "audio/midi",
"rnc": "application/relax-ng-compact-syntax",
"rng": "application/xml",
"roa": "application/rpki-roa",
"roff": "text/troff",
"rq": "application/sparql-query",
"rs": "application/rls-services+xml",
"rsat": "application/atsc-rsat+xml",
"rsd": "application/rsd+xml",
"rsheet": "application/urc-ressheet+xml",
"rss": "application/rss+xml",
"rtf": "text/rtf",
"rtx": "text/richtext",
"rusd": "application/route-usd+xml",
"s3m": "audio/s3m",
"sbml": "application/sbml+xml",
"scq": "application/scvp-cv-request",
"scs": "application/scvp-cv-response",
"sdp": "application/sdp",
"senmlx": "application/senml+xml",
"sensmlx": "application/sensml+xml",
"ser": "application/java-serialized-object",
"setpay": "application/set-payment-initiation",
"setreg": "application/set-registration-initiation",
"sgi": "image/sgi",
"sgm": "text/sgml",
"sgml": "text/sgml",
"shex": "text/shex",
"shf": "application/shf+xml",
"shtml": "text/html",
"sieve": "application/sieve",
"sig": "application/pgp-signature",
"sil": "audio/silk",
"silo": "model/mesh",
"siv": "application/sieve",
"slim": "text/slim",
"slm": "text/slim",
"sls": "application/route-s-tsid+xml",
"smi": "application/smil+xml",
"smil": "application/smil+xml",
"snd": "audio/basic",
"so": "application/octet-stream",
"spdx": "text/spdx",
"spp": "application/scvp-vp-response",
"spq": "application/scvp-vp-request",
"spx": "audio/ogg",
"sql": "application/sql",
"sru": "application/sru+xml",
"srx": "application/sparql-results+xml",
"ssdl": "application/ssdl+xml",
"ssml": "application/ssml+xml",
"stk": "application/hyperstudio",
"stl": "model/stl",
"stpx": "model/step+xml",
"stpxz": "model/step-xml+zip",
"stpz": "model/step+zip",
"styl": "text/stylus",
"stylus": "text/stylus",
"svg": "image/svg+xml",
"svgz": "image/svg+xml",
"swidtag": "application/swid+xml",
"t": "text/troff",
"t38": "image/t38",
"td": "application/urc-targetdesc+xml",
"tei": "application/tei+xml",
"teicorpus": "application/tei+xml",
"text": "text/plain",
"tfi": "application/thraud+xml",
"tfx": "image/tiff-fx",
"tif": "image/tiff",
"tiff": "image/tiff",
"toml": "application/toml",
"tr": "text/troff",
"trig": "application/trig",
"ts": "video/mp2t",
"tsd": "application/timestamped-data",
"tsv": "text/tab-separated-values",
"ttc": "font/collection",
"ttf": "font/ttf",
"ttl": "text/turtle",
"ttml": "application/ttml+xml",
"txt": "text/plain",
"u3d": "model/u3d",
"u8dsn": "message/global-delivery-status",
"u8hdr": "message/global-headers",
"u8mdn": "message/global-disposition-notification",
"u8msg": "message/global",
"ubj": "application/ubjson",
"uri": "text/uri-list",
"uris": "text/uri-list",
"urls": "text/uri-list",
"vcard": "text/vcard",
"vrml": "model/vrml",
"vtt": "text/vtt",
"vxml": "application/voicexml+xml",
"war": "application/java-archive",
"wasm": "application/wasm",
"wav": "audio/wav",
"weba": "audio/webm",
"webm": "video/webm",
"webmanifest": "application/manifest+json",
"webp": "image/webp",
"wgsl": "text/wgsl",
"wgt": "application/widget",
"wif": "application/watcherinfo+xml",
"wmf": "image/wmf",
"woff": "font/woff",
"woff2": "font/woff2",
"wrl": "model/vrml",
"wsdl": "application/wsdl+xml",
"wspolicy": "application/wspolicy+xml",
"x3d": "model/x3d+xml",
"x3db": "model/x3d+fastinfoset",
"x3dbz": "model/x3d+binary",
"x3dv": "model/x3d-vrml",
"x3dvz": "model/x3d+vrml",
"x3dz": "model/x3d+xml",
"xaml": "application/xaml+xml",
"xav": "application/xcap-att+xml",
"xca": "application/xcap-caps+xml",
"xcs": "application/calendar+xml",
"xdf": "application/xcap-diff+xml",
"xdssc": "application/dssc+xml",
"xel": "application/xcap-el+xml",
"xenc": "application/xenc+xml",
"xer": "application/patch-ops-error+xml",
"xfdf": "application/xfdf",
"xht": "application/xhtml+xml",
"xhtml": "application/xhtml+xml",
"xhvml": "application/xv+xml",
"xlf": "application/xliff+xml",
"xm": "audio/xm",
"xml": "text/xml",
"xns": "application/xcap-ns+xml",
"xop": "application/xop+xml",
"xpl": "application/xproc+xml",
"xsd": "application/xml",
"xsf": "application/prs.xsf+xml",
"xsl": "application/xml",
"xslt": "application/xml",
"xspf": "application/xspf+xml",
"xvm": "application/xv+xml",
"xvml": "application/xv+xml",
"yaml": "text/yaml",
"yang": "application/yang",
"yin": "application/yin+xml",
"yml": "text/yaml",
"zip": "application/zip"
};
function lookup(extn) {
let tmp = ("" + extn).trim().toLowerCase();
let idx = tmp.lastIndexOf(".");
return mimes[!~idx ? tmp : tmp.substring(++idx)];
}
//#endregion
//#region src/utils.ts
const createTrieNode = () => ({
children: /* @__PURE__ */ new Map(),
targets: []
});
const createPathNormalizer = () => {
const pathCache = /* @__PURE__ */ new Map();
return (filePath) => {
if (!pathCache.has(filePath)) {
const normalizedPath = path.normalize(filePath).toLowerCase().replace(/\\/g, "/");
pathCache.set(filePath, normalizedPath);
}
return pathCache.get(filePath);
};
};
const splitPath = function* (normalizedPath) {
const cleaned = normalizedPath.replace(/^\/+|\/+$/g, "");
if (cleaned === "") return;
let start = 0;
while (start < cleaned.length) {
const idx = cleaned.indexOf("/", start);
if (idx === -1) {
yield cleaned.slice(start);
break;
}
yield cleaned.slice(start, idx);
start = idx + 1;
}
};
const groupTargetsByDirectoryTree = (targets) => {
if (targets.length === 0) return [];
if (targets.length === 1) return [targets];
const targetsWithOrder = targets.map((target, order) => ({
...target,
order
}));
const getNormalizedPath = createPathNormalizer();
const root = createTrieNode();
for (const target of targetsWithOrder) {
const segments = splitPath(getNormalizedPath(target.resolvedDest));
let currentNode = root;
for (const segment of segments) {
if (!currentNode.children.has(segment)) currentNode.children.set(segment, createTrieNode());
currentNode = currentNode.children.get(segment);
}
currentNode.targets.push(target);
}
const groups = [];
const collectGroups = (node) => {
if (node.children.size === 0) return node.targets;
if (node.targets.length > 0) {
const allTargets = [...node.targets];
for (const child of node.children.values()) {
const childTargets = collectTargets(child);
if (childTargets.length > 0) allTargets.push(...childTargets);
}
return allTargets;
}
for (const child of node.children.values()) {
const childTargets = collectGroups(child);
if (childTargets.length > 0) groups.push(childTargets);
}
return [];
};
const collectTargets = (node) => {
if (node.children.size === 0) return node.targets;
const allTargets = [...node.targets];
for (const child of node.children.values()) {
const childTargets = collectTargets(child);
if (childTargets.length > 0) allTargets.push(...childTargets);
}
return allTargets;
};
const rootTargets = collectGroups(root);
if (rootTargets.length > 0) groups.push(rootTargets);
for (const group of groups) group.sort((a, b) => a.order - b.order);
return groups;
};
async function renameTarget(target, rename, src) {
const parsedPath = path.parse(target);
if (typeof rename === "string") return rename;
return rename(parsedPath.name, parsedPath.ext.replace(".", ""), src);
}
const collectCopyTargets = async (root, targets, structured, silent) => {
const copyTargets = [];
for (const target of targets) {
const { src, dest, rename, transform, preserveTimestamps, dereference, overwrite } = target;
const matchedPaths = await glob(src, {
onlyFiles: false,
dot: true,
expandDirectories: false,
cwd: root
});
if (matchedPaths.length === 0 && !silent) throw new Error(`No file was found to copy on ${src} src.`);
for (const matchedPath of matchedPaths) {
const relativeMatchedPath = path.isAbsolute(matchedPath) ? path.relative(root, matchedPath) : matchedPath;
const absoluteMatchedPath = path.resolve(root, matchedPath);
if (transform) {
if (!(await fs.stat(absoluteMatchedPath)).isFile()) throw new Error(`"transform" option only supports a file: '${relativeMatchedPath}' is not a file`);
}
const { base, dir } = path.parse(relativeMatchedPath);
let destDir;
if (!structured || !dir) destDir = dest;
else destDir = `${dest}/${dir.replace(/^(?:\.\.\/)+/, "")}`.replace(/^\/+|\/+$/g, "");
copyTargets.push({
src: relativeMatchedPath,
dest: path.join(destDir, rename ? await renameTarget(base, rename, absoluteMatchedPath) : base),
transform,
preserveTimestamps: preserveTimestamps ?? false,
dereference: dereference ?? true,
overwrite: overwrite ?? true
});
}
}
return copyTargets;
};
async function getTransformedContent(file, transform) {
if (transform.encoding === "buffer") {
const content$1 = await fs.readFile(file);
return transform.handler(content$1, file);
}
const content = await fs.readFile(file, transform.encoding);
return transform.handler(content, file);
}
async function transformCopy(transform, src, dest, overwrite) {
if (overwrite === false || overwrite === "error") {
if (await fsExists(dest)) {
if (overwrite === false) return { copied: false };
if (overwrite === "error") throw new Error(`File ${dest} already exists`);
}
}
const transformedContent = await getTransformedContent(src, transform);
if (transformedContent === null) return { copied: false };
await fs.mkdir(path.dirname(dest), { recursive: true });
await fs.writeFile(dest, transformedContent);
return { copied: true };
}
const copyAll = async (rootSrc, rootDest, targets, structured, silent) => {
const copyTargets = await collectCopyTargets(rootSrc, targets, structured, silent);
const groups = groupTargetsByDirectoryTree(copyTargets.map((target) => ({
...target,
resolvedSrc: path.resolve(rootSrc, target.src),
resolvedDest: path.resolve(rootSrc, rootDest, target.dest)
})));
let copiedCount = 0;
await pMap(groups, async (targetGroup) => {
for (const resolvedTarget of targetGroup) {
const { resolvedSrc, resolvedDest, transform, preserveTimestamps, dereference, overwrite } = resolvedTarget;
const transformOption = resolveTransformOption(transform);
if (transformOption) {
if ((await transformCopy(transformOption, resolvedSrc, resolvedDest, overwrite)).copied) copiedCount++;
} else {
await fs.cp(resolvedSrc, resolvedDest, {
recursive: true,
preserveTimestamps,
dereference,
force: overwrite === true,
errorOnExist: overwrite === "error"
});
copiedCount++;
}
}
}, { concurrency: 5 });
return {
targets: copyTargets.length,
copied: copiedCount
};
};
const updateFileMapFromTargets = (targets, fileMap, absoluteBuildOutDir) => {
fileMap.clear();
for (const target of [...targets].reverse()) {
let dest = path.isAbsolute(target.dest) ? path.relative(absoluteBuildOutDir, target.dest) : target.dest;
dest = dest.replace(/\\/g, "/");
if (path.isAbsolute(dest) || dest.startsWith("../")) continue;
if (dest.startsWith("./")) dest = dest.slice(1);
else if (!dest.startsWith("/")) dest = `/${dest}`;
if (!fileMap.has(dest)) fileMap.set(dest, []);
fileMap.get(dest).push({
src: target.src,
dest: target.dest,
overwrite: target.overwrite,
transform: target.transform
});
}
};
const calculateMd5Base64 = (content) => createHash("md5").update(content).digest("base64");
const formatConsole = (msg) => `${pc.cyan("[vite-plugin-static-copy]")} ${msg}`;
const outputCollectedLog = (logger, collectedMap) => {
if (collectedMap.size > 0) {
logger.info(formatConsole(pc.green(`Collected ${collectedMap.size} items.`)));
if (process.env.DEBUG === "vite:plugin-static-copy") for (const [key, vals] of collectedMap) for (const val of vals) logger.info(formatConsole(` - '${key}' -> '${val.src}'${val.transform ? " (with content transform)" : ""}`));
} else logger.warn(formatConsole(pc.yellow("No items found.")));
};
const outputCopyLog = (logger, result) => {
if (result.targets > 0) {
const copiedMessage = pc.green(`Copied ${result.copied} items.`);
const skipped = result.targets - result.copied;
const skippedMessage = skipped > 0 ? ` ${pc.gray(`(Skipped ${skipped} items.)`)}` : "";
logger.info(formatConsole(`${copiedMessage}${skippedMessage}`));
}
};
function resolveTransformOption(transformOption) {
if (typeof transformOption === "function") return {
handler: transformOption,
encoding: "utf8"
};
return transformOption;
}
async function fsExists(p) {
try {
await fs.stat(p);
} catch (e) {
if (e.code === "ENOENT") return false;
throw e;
}
return true;
}
//#endregion
//#region src/middleware.ts
const knownJavascriptExtensionRE = /\.(?:[tj]sx?|[cm][tj]s)$/;
function shouldServeOverwriteCheck(overwrite, srcAbsolutePath, root, publicDir, dest) {
if (overwrite === true || publicDir === "") return true;
const publicFile = resolve(publicDir, dest);
if (existsSync(publicFile)) {
if (overwrite === "error" && existsSync(srcAbsolutePath)) {
const destAbsolutePath = resolve(root, dest);
throw new Error(`File ${destAbsolutePath} will be copied from ${publicFile} (overwrite option is set to "error")`);
}
return false;
}
return true;
}
function isFileInside(filepath, srcBase) {
const srcBaseWithTrailingSlash = srcBase.endsWith(sep) ? srcBase : `${srcBase}${sep}`;
return filepath.startsWith(srcBaseWithTrailingSlash);
}
function viaLocal(root, publicDir, fileMap, uri) {
if (uri.endsWith("/")) uri = uri.slice(0, -1);
const files = fileMap.get(uri);
if (files && files[0]) {
const file = files[0];
const filepath = resolve(root, file.src);
if (shouldServeOverwriteCheck(file.overwrite, filepath, root, publicDir, file.dest) === false) return;
return {
filepath,
stats: statSync(filepath),
transform: file.transform
};
}
for (const [key, vals] of fileMap) {
const dir = key.endsWith("/") ? key : `${key}/`;
if (!uri.startsWith(dir)) continue;
for (const val of vals) {
const srcBase = resolve(root, val.src);
const filepath = resolve(srcBase, uri.slice(dir.length));
if (!isFileInside(filepath, srcBase)) return;
if (shouldServeOverwriteCheck(val.overwrite, filepath, root, publicDir, join(val.dest, uri.slice(dir.length))) === false) return;
const stats = statSync(filepath, { throwIfNoEntry: false });
if (stats) return {
filepath,
stats
};
}
return;
}
}
function getStaticHeaders(stats) {
return {
"Content-Length": stats.size,
"Last-Modified": stats.mtime.toUTCString(),
ETag: `W/"${stats.size}-${stats.mtime.getTime()}"`,
"Cache-Control": "no-cache"
};
}
function getTransformHeaders(encoding, content) {
return {
"Content-Length": Buffer.byteLength(content, encoding === "buffer" ? void 0 : encoding),
ETag: `W/"${calculateMd5Base64(content)}"`,
"Cache-Control": "no-cache"
};
}
function getMergeHeaders(headers, res) {
headers = { ...headers };
for (const key in headers) {
const tmp = res.getHeader(key);
if (tmp) headers[key] = tmp;
}
const contentTypeHeader = res.getHeader("content-type");
if (contentTypeHeader) headers["Content-Type"] = contentTypeHeader;
return headers;
}
function sendStatic(req, res, file, stats) {
const staticHeaders = getStaticHeaders(stats);
if (req.headers["if-none-match"] === staticHeaders["ETag"]) {
res.writeHead(304);
res.end();
return;
}
let code = 200;
const headers = getMergeHeaders(staticHeaders, res);
const opts = {};
if (req.headers.range) {
code = 206;
const [x, y] = req.headers.range.replace("bytes=", "").split("-");
let end = (y ? parseInt(y, 10) : 0) || stats.size - 1;
const start = (x ? parseInt(x, 10) : 0) || 0;
opts.end = end;
opts.start = start;
if (end >= stats.size) end = stats.size - 1;
if (start >= stats.size) {
res.setHeader("Content-Range", `bytes */${stats.size}`);
res.statusCode = 416;
res.end();
return;
}
headers["Content-Range"] = `bytes ${start}-${end}/${stats.size}`;
headers["Content-Length"] = end - start + 1;
headers["Accept-Ranges"] = "bytes";
}
res.writeHead(code, headers);
createReadStream(file, opts).pipe(res);
}
function sendTransform(req, res, transform, transformedContent) {
const transformHeaders = getTransformHeaders(transform.encoding, transformedContent);
if (req.headers["if-none-match"] === transformHeaders["ETag"]) {
res.writeHead(304);
res.end();
return;
}
const code = 200;
const headers = getMergeHeaders(transformHeaders, res);
res.writeHead(code, headers);
res.end(transformedContent);
}
function setHeaders(res, pathname, headers) {
if (knownJavascriptExtensionRE.test(pathname)) res.setHeader("Content-Type", "text/javascript");
else {
let ctype = lookup(pathname) || "";
if (ctype === "text/html") ctype += ";charset=utf-8";
res.setHeader("Content-Type", ctype);
}
if (headers) for (const name in headers) res.setHeader(name, headers[name]);
}
function return404(res, next) {
if (next) {
next();
return;
}
res.statusCode = 404;
res.end();
}
function serveStaticCopyMiddleware({ root, publicDir, server }, fileMap) {
return async function viteServeStaticCopyMiddleware(req, res, next) {
let pathname = parse(req).pathname;
if (pathname.includes("%")) try {
pathname = decodeURI(pathname);
} catch {}
try {
const data = viaLocal(root, publicDir, fileMap, pathname);
if (!data || data.stats.isDirectory()) {
return404(res, next);
return;
}
const transformOption = resolveTransformOption(data.transform);
if (transformOption) {
const transformedContent = await getTransformedContent(data.filepath, transformOption);
if (transformedContent === null) {
return404(res, next);
return;
}
setHeaders(res, pathname, server.headers);
sendTransform(req, res, transformOption, transformedContent);
return;
}
setHeaders(res, pathname, server.headers);
sendStatic(req, res, data.filepath, data.stats);
} catch (e) {
if (e instanceof Error) {
next(e);
return;
}
throw e;
}
};
}
//#endregion
//#region node_modules/.pnpm/throttle-debounce@5.0.2/node_modules/throttle-debounce/esm/index.js
/**
* Throttle execution of a function. Especially useful for rate limiting
* execution of handlers on events like resize and scroll.
*
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher)
* are most useful.
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through,
* as-is, to `callback` when the throttled-function is executed.
* @param {object} [options] - An object to configure options.
* @param {boolean} [options.noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds
* while the throttled-function is being called. If noTrailing is false or unspecified, callback will be executed
* one final time after the last throttled-function call. (After the throttled-function has not been called for
* `delay` milliseconds, the internal counter is reset).
* @param {boolean} [options.noLeading] - Optional, defaults to false. If noLeading is false, the first throttled-function call will execute callback
* immediately. If noLeading is true, the first the callback execution will be skipped. It should be noted that
* callback will never executed if both noLeading = true and noTrailing = true.
* @param {boolean} [options.debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is
* false (at end), schedule `callback` to execute after `delay` ms.
*
* @returns {Function} A new, throttled, function.
*/
function throttle(delay, callback, options) {
var _ref = options || {}, _ref$noTrailing = _ref.noTrailing, noTrailing = _ref$noTrailing === void 0 ? false : _ref$noTrailing, _ref$noLeading = _ref.noLeading, noLeading = _ref$noLeading === void 0 ? false : _ref$noLeading, _ref$debounceMode = _ref.debounceMode, debounceMode = _ref$debounceMode === void 0 ? void 0 : _ref$debounceMode;
var timeoutID;
var cancelled = false;
var lastExec = 0;
function clearExistingTimeout() {
if (timeoutID) clearTimeout(timeoutID);
}
function cancel(options$1) {
var _ref2$upcomingOnly = (options$1 || {}).upcomingOnly, upcomingOnly = _ref2$upcomingOnly === void 0 ? false : _ref2$upcomingOnly;
clearExistingTimeout();
cancelled = !upcomingOnly;
}
function wrapper() {
for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) arguments_[_key] = arguments[_key];
var self = this;
var elapsed = Date.now() - lastExec;
if (cancelled) return;
function exec() {
lastExec = Date.now();
callback.apply(self, arguments_);
}
function clear() {
timeoutID = void 0;
}
if (!noLeading && debounceMode && !timeoutID) exec();
clearExistingTimeout();
if (debounceMode === void 0 && elapsed > delay) if (noLeading) {
lastExec = Date.now();
if (!noTrailing) timeoutID = setTimeout(debounceMode ? clear : exec, delay);
} else exec();
else if (noTrailing !== true) timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === void 0 ? delay - elapsed : delay);
}
wrapper.cancel = cancel;
return wrapper;
}
/**
* Debounce execution of a function. Debouncing, unlike throttling,
* guarantees that a function is only executed a single time, either at the
* very beginning of a series of calls, or at the very end.
*
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
* to `callback` when the debounced-function is executed.
* @param {object} [options] - An object to configure options.
* @param {boolean} [options.atBegin] - Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds
* after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.
* (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).
*
* @returns {Function} A new, debounced function.
*/
function debounce(delay, callback, options) {
var _ref$atBegin = (options || {}).atBegin;
return throttle(delay, callback, { debounceMode: (_ref$atBegin === void 0 ? false : _ref$atBegin) !== false });
}
//#endregion
//#region src/serve.ts
const servePlugin = ({ targets, structured, watch, silent, environment }) => {
let config;
let watcher;
const fileMap = /* @__PURE__ */ new Map();
const collectFileMap = async () => {
const absoluteBuildOutDir = path.resolve(config.root, config.build.outDir);
try {
updateFileMapFromTargets(await collectCopyTargets(config.root, targets, structured, silent), fileMap, absoluteBuildOutDir);
} catch (e) {
if (!silent) config.logger.error(formatConsole(pc.red(e.toString())));
}
};
const collectFileMapDebounce = debounce(100, async () => {
await collectFileMap();
});
return {
name: "vite-plugin-static-copy:serve",
apply: "serve",
configResolved(_config) {
config = _config;
},
async buildStart() {
if (this.environment && this.environment.name !== environment) return;
await collectFileMap();
},
configureServer({ httpServer, middlewares, ws }) {
const reloadPage = () => {
ws.send({
type: "full-reload",
path: "*"
});
};
watcher = chokidar.watch(targets.flatMap((target) => target.src), {
cwd: config.root,
ignoreInitial: true,
...watch.options
});
watcher.on("add", async (path$1) => {
if (!silent) config.logger.info(formatConsole(`${pc.green("detected new file")} ${path$1}`), { timestamp: true });
await collectFileMapDebounce();
if (watch.reloadPageOnChange) reloadPage();
});
if (watch.reloadPageOnChange) {
watcher.on("change", (path$1) => {
if (!silent) config.logger.info(formatConsole(`${pc.green("file changed")} ${path$1}`), { timestamp: true });
reloadPage();
});
watcher.on("unlink", (path$1) => {
if (!silent) config.logger.info(formatConsole(`${pc.green("file deleted")} ${path$1}`), { timestamp: true });
reloadPage();
});
}
if (!silent) httpServer?.once("listening", () => {
setTimeout(() => {
outputCollectedLog(config.logger, fileMap);
}, 0);
});
return () => {
const middleware = serveStaticCopyMiddleware(config, fileMap);
middlewares.use(middleware);
const targetMiddlewareIndex = findMiddlewareIndex(middlewares.stack, ["viteServePublicMiddleware", "viteTransformMiddleware"]);
const serveStaticCopyMiddlewareIndex = middlewares.stack.findIndex((item) => item.handle === middleware);
const serveStaticCopyMiddlewareItem = middlewares.stack.splice(serveStaticCopyMiddlewareIndex, 1)[0];
if (serveStaticCopyMiddlewareItem === void 0) throw new Error();
middlewares.stack.splice(targetMiddlewareIndex, 0, serveStaticCopyMiddlewareItem);
};
},
async closeBundle() {
if (this.environment && this.environment.name !== environment) return;
await watcher.close();
}
};
};
const findMiddlewareIndex = (stack, names) => {
const ns = Array.isArray(names) ? names : [names];
for (const name of ns) {
const index = stack.findIndex((middleware) => typeof middleware.handle === "function" && middleware.handle.name === name);
if (index > 0) return index;
}
return -1;
};
//#endregion
//#region src/build.ts
const buildPlugin = ({ targets, structured, silent, hook, environment }) => {
let config;
let output = false;
return {
name: "vite-plugin-static-copy:build",
apply: "build",
configResolved(_config) {
config = _config;
},
buildEnd() {
if (this.environment && this.environment.name !== environment) return;
output = false;
},
async [hook]() {
if (this.environment && this.environment.name !== environment) return;
if (output) return;
output = true;
const result = await copyAll(config.root, config.build.outDir, targets, structured, silent);
if (!silent) outputCopyLog(config.logger, result);
}
};
};
//#endregion
//#region src/index.ts
const viteStaticCopy = (options) => {
const resolvedOptions = resolveOptions(options);
return [servePlugin(resolvedOptions), buildPlugin(resolvedOptions)];
};
//#endregion
export { viteStaticCopy };