feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+44
View File
@@ -0,0 +1,44 @@
# vite-plugin-remote-assets
[![NPM version](https://img.shields.io/npm/v/vite-plugin-remote-assets?color=a1b858&label=)](https://www.npmjs.com/package/vite-plugin-remote-assets)
Bundles your assets from remote URLs with your app
```html
<img src="http://example.com/image.jpg" />
```
To
```html
<img src="/node_modules/.remote-assets/f83j2f.jpg" />
```
## Install
```bash
npm i -D vite-plugin-remote-assets
```
```ts
// vite.config.ts
import RemoteAssets from 'vite-plugin-remote-assets'
export default {
plugins: [
RemoteAssets()
]
}
```
## Sponsors
<p align="center">
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
</a>
</p>
## License
[MIT](./LICENSE) License © 2021 [Anthony Fu](https://github.com/antfu)
+218
View File
@@ -0,0 +1,218 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
DefaultRules: () => DefaultRules,
VitePluginRemoteAssets: () => VitePluginRemoteAssets,
default: () => src_default
});
module.exports = __toCommonJS(src_exports);
var import_node_path = require("path");
var import_utils = require("@antfu/utils");
var import_axios = __toESM(require("axios"), 1);
var import_blueimp_md5 = __toESM(require("blueimp-md5"), 1);
var import_debug = __toESM(require("debug"), 1);
var import_fs_extra = __toESM(require("fs-extra"), 1);
var import_magic_string = __toESM(require("magic-string"), 1);
var import_vite = require("vite");
var DefaultRules = [
{
match: /\b(https?:\/\/[\w#&?./-]*?\.(?:png|jpe?g|svg|ico))(?=[`'")\]])/gi
}
];
function isValidHttpUrl(str) {
let url;
try {
url = new URL(str);
} catch {
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}
function sleep(seconds) {
return new Promise((resolve2) => setTimeout(resolve2, seconds * 1e3));
}
var debug = (0, import_debug.default)("vite-plugin-remote-assets");
function VitePluginRemoteAssets(options = {}) {
const {
assetsDir = "node_modules/.remote-assets",
rules = DefaultRules,
resolveMode = "relative",
awaitDownload = true,
retryTooManyRequests = false
} = options;
let dir = void 0;
let config;
const envs = [];
async function downloadTo(url, filepath, { retryTooManyRequests: retryTooManyRequests2 }) {
const writer = import_fs_extra.default.createWriteStream(filepath);
const response = await (0, import_axios.default)({
url,
method: "GET",
validateStatus: (status) => {
if (status >= 200 && status < 300)
return true;
else if (retryTooManyRequests2 && status === 429)
return true;
else
return false;
},
responseType: "stream"
});
if (response.status === 429) {
const retryAfter = response.headers["retry-after"];
if (!retryAfter) {
throw new Error(`${url}: 429 without retry-after header`);
} else {
debug(`${url}: 429, retry after ${retryAfter} seconds`);
await sleep(retryAfter);
return await downloadTo(url, filepath, { retryTooManyRequests: retryTooManyRequests2 });
}
}
response.data.pipe(writer);
return new Promise((resolve2, reject) => {
writer.on("finish", resolve2);
writer.on("error", reject);
});
}
const tasksMap = {};
async function transform(code, id) {
const tasks = [];
const s = new import_magic_string.default(code);
let hasReplaced = false;
let match;
for (const rule of rules) {
rule.match.lastIndex = 0;
while (match = rule.match.exec(code)) {
const start = match.index;
const end = start + match[0].length;
const url = match[0];
if (!url || !isValidHttpUrl(url))
continue;
const hash = (0, import_blueimp_md5.default)(url) + (rule.ext || (0, import_node_path.extname)(url));
const filepath = (0, import_utils.slash)((0, import_node_path.resolve)(dir, hash));
debug("detected", url, hash);
if (!import_fs_extra.default.existsSync(filepath) || tasksMap[filepath]) {
if (!tasksMap[filepath]) {
tasksMap[filepath] = (async () => {
try {
debug("downloading", url);
await downloadTo(url, filepath, { retryTooManyRequests });
debug("downloaded", url);
} catch (e) {
if (import_fs_extra.default.existsSync(filepath))
await import_fs_extra.default.unlink(filepath);
throw e;
} finally {
delete tasksMap[filepath];
}
})();
}
tasks.push(tasksMap[filepath]);
if (!awaitDownload)
continue;
}
hasReplaced = true;
const mode = typeof resolveMode === "function" ? resolveMode(id, url) : resolveMode;
let newUrl;
if (mode === "relative") {
newUrl = (0, import_utils.slash)((0, import_node_path.relative)((0, import_node_path.dirname)(id), `${dir}/${hash}`));
if (newUrl[0] !== ".")
newUrl = `./${newUrl}`;
} else {
let path = `${dir}/${hash}`;
if (!path.startsWith("/"))
path = `/${path}`;
newUrl = `/@fs${path}`;
}
s.overwrite(start, end, newUrl);
}
}
if (tasks.length) {
if (awaitDownload) {
await Promise.all(tasks);
} else {
Promise.all(tasks).then(() => {
envs.forEach((env) => {
if (env instanceof import_vite.DevEnvironment) {
const module3 = env.moduleGraph.getModuleById(id);
if (module3)
env.moduleGraph.invalidateModule(module3);
return;
}
const module2 = env.moduleGraph.getModuleById(id);
if (module2)
env.moduleGraph.invalidateModule(module2);
});
});
}
}
if (!hasReplaced)
return null;
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: true }) : null
};
}
return {
name: "vite-plugin-remote-assets",
enforce: "pre",
async configResolved(_config) {
config = _config;
dir = (0, import_utils.slash)((0, import_node_path.resolve)(config.root, assetsDir));
if ("force" in config.server && config.server.force || config.optimizeDeps.force)
await import_fs_extra.default.emptyDir(dir);
await import_fs_extra.default.ensureDir(dir);
},
configureServer(server) {
if ("environments" in server && typeof server.environments === "object" && server.environments != null) {
Object.values(server.environments).forEach((env) => envs.push(env));
} else {
envs.push(server);
}
},
async transform(code, id) {
return await transform(code, id);
},
transformIndexHtml: {
order: "pre",
async handler(code, ctx) {
return (await transform(code, ctx.filename))?.code;
}
}
};
}
var src_default = VitePluginRemoteAssets;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
DefaultRules,
VitePluginRemoteAssets
});
+48
View File
@@ -0,0 +1,48 @@
import { Plugin } from 'vite';
interface RemoteAssetsRule {
/**
* Regex to match urls, should be http:// or https://
*/
match: RegExp;
/**
* Extension for the url, should by leading with `.`
*
* When not specified, if will try to infer from the url.
*/
ext?: string;
}
interface RemoteAssetsOptions {
/**
* Directory name to store the assets from remote
*
* @default 'node_modules/.remote-assets'
*/
assetsDir?: string;
/**
* Rules to match urls to replace
*/
rules?: RemoteAssetsRule[];
/**
* Mode to resolve urls
*
* @default relative
*/
resolveMode?: 'relative' | '@fs' | ((moduleId: string, url: string) => 'relative' | '@fs');
/**
* Wait for download before serving the content
*
* @default true
*/
awaitDownload?: boolean;
/**
* If download returns 429, use the retry-after header to wait and retry
*
* @default false
*/
retryTooManyRequests?: boolean;
}
declare const DefaultRules: RemoteAssetsRule[];
declare function VitePluginRemoteAssets(options?: RemoteAssetsOptions): Plugin;
export { DefaultRules, type RemoteAssetsOptions, type RemoteAssetsRule, VitePluginRemoteAssets, VitePluginRemoteAssets as default };
+48
View File
@@ -0,0 +1,48 @@
import { Plugin } from 'vite';
interface RemoteAssetsRule {
/**
* Regex to match urls, should be http:// or https://
*/
match: RegExp;
/**
* Extension for the url, should by leading with `.`
*
* When not specified, if will try to infer from the url.
*/
ext?: string;
}
interface RemoteAssetsOptions {
/**
* Directory name to store the assets from remote
*
* @default 'node_modules/.remote-assets'
*/
assetsDir?: string;
/**
* Rules to match urls to replace
*/
rules?: RemoteAssetsRule[];
/**
* Mode to resolve urls
*
* @default relative
*/
resolveMode?: 'relative' | '@fs' | ((moduleId: string, url: string) => 'relative' | '@fs');
/**
* Wait for download before serving the content
*
* @default true
*/
awaitDownload?: boolean;
/**
* If download returns 429, use the retry-after header to wait and retry
*
* @default false
*/
retryTooManyRequests?: boolean;
}
declare const DefaultRules: RemoteAssetsRule[];
declare function VitePluginRemoteAssets(options?: RemoteAssetsOptions): Plugin;
export { DefaultRules, type RemoteAssetsOptions, type RemoteAssetsRule, VitePluginRemoteAssets, VitePluginRemoteAssets as default };
+182
View File
@@ -0,0 +1,182 @@
// src/index.ts
import { dirname, extname, relative, resolve } from "node:path";
import { slash } from "@antfu/utils";
import axios from "axios";
import md5 from "blueimp-md5";
import Debug from "debug";
import fs from "fs-extra";
import MagicString from "magic-string";
import { DevEnvironment } from "vite";
var DefaultRules = [
{
match: /\b(https?:\/\/[\w#&?./-]*?\.(?:png|jpe?g|svg|ico))(?=[`'")\]])/gi
}
];
function isValidHttpUrl(str) {
let url;
try {
url = new URL(str);
} catch {
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}
function sleep(seconds) {
return new Promise((resolve2) => setTimeout(resolve2, seconds * 1e3));
}
var debug = Debug("vite-plugin-remote-assets");
function VitePluginRemoteAssets(options = {}) {
const {
assetsDir = "node_modules/.remote-assets",
rules = DefaultRules,
resolveMode = "relative",
awaitDownload = true,
retryTooManyRequests = false
} = options;
let dir = void 0;
let config;
const envs = [];
async function downloadTo(url, filepath, { retryTooManyRequests: retryTooManyRequests2 }) {
const writer = fs.createWriteStream(filepath);
const response = await axios({
url,
method: "GET",
validateStatus: (status) => {
if (status >= 200 && status < 300)
return true;
else if (retryTooManyRequests2 && status === 429)
return true;
else
return false;
},
responseType: "stream"
});
if (response.status === 429) {
const retryAfter = response.headers["retry-after"];
if (!retryAfter) {
throw new Error(`${url}: 429 without retry-after header`);
} else {
debug(`${url}: 429, retry after ${retryAfter} seconds`);
await sleep(retryAfter);
return await downloadTo(url, filepath, { retryTooManyRequests: retryTooManyRequests2 });
}
}
response.data.pipe(writer);
return new Promise((resolve2, reject) => {
writer.on("finish", resolve2);
writer.on("error", reject);
});
}
const tasksMap = {};
async function transform(code, id) {
const tasks = [];
const s = new MagicString(code);
let hasReplaced = false;
let match;
for (const rule of rules) {
rule.match.lastIndex = 0;
while (match = rule.match.exec(code)) {
const start = match.index;
const end = start + match[0].length;
const url = match[0];
if (!url || !isValidHttpUrl(url))
continue;
const hash = md5(url) + (rule.ext || extname(url));
const filepath = slash(resolve(dir, hash));
debug("detected", url, hash);
if (!fs.existsSync(filepath) || tasksMap[filepath]) {
if (!tasksMap[filepath]) {
tasksMap[filepath] = (async () => {
try {
debug("downloading", url);
await downloadTo(url, filepath, { retryTooManyRequests });
debug("downloaded", url);
} catch (e) {
if (fs.existsSync(filepath))
await fs.unlink(filepath);
throw e;
} finally {
delete tasksMap[filepath];
}
})();
}
tasks.push(tasksMap[filepath]);
if (!awaitDownload)
continue;
}
hasReplaced = true;
const mode = typeof resolveMode === "function" ? resolveMode(id, url) : resolveMode;
let newUrl;
if (mode === "relative") {
newUrl = slash(relative(dirname(id), `${dir}/${hash}`));
if (newUrl[0] !== ".")
newUrl = `./${newUrl}`;
} else {
let path = `${dir}/${hash}`;
if (!path.startsWith("/"))
path = `/${path}`;
newUrl = `/@fs${path}`;
}
s.overwrite(start, end, newUrl);
}
}
if (tasks.length) {
if (awaitDownload) {
await Promise.all(tasks);
} else {
Promise.all(tasks).then(() => {
envs.forEach((env) => {
if (env instanceof DevEnvironment) {
const module2 = env.moduleGraph.getModuleById(id);
if (module2)
env.moduleGraph.invalidateModule(module2);
return;
}
const module = env.moduleGraph.getModuleById(id);
if (module)
env.moduleGraph.invalidateModule(module);
});
});
}
}
if (!hasReplaced)
return null;
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: true }) : null
};
}
return {
name: "vite-plugin-remote-assets",
enforce: "pre",
async configResolved(_config) {
config = _config;
dir = slash(resolve(config.root, assetsDir));
if ("force" in config.server && config.server.force || config.optimizeDeps.force)
await fs.emptyDir(dir);
await fs.ensureDir(dir);
},
configureServer(server) {
if ("environments" in server && typeof server.environments === "object" && server.environments != null) {
Object.values(server.environments).forEach((env) => envs.push(env));
} else {
envs.push(server);
}
},
async transform(code, id) {
return await transform(code, id);
},
transformIndexHtml: {
order: "pre",
async handler(code, ctx) {
return (await transform(code, ctx.filename))?.code;
}
}
};
}
var src_default = VitePluginRemoteAssets;
export {
DefaultRules,
VitePluginRemoteAssets,
src_default as default
};
+500
View File
@@ -0,0 +1,500 @@
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var __accessCheck = (obj, member, msg) => {
if (!member.has(obj))
throw TypeError("Cannot " + msg);
};
var __privateGet = (obj, member, getter) => {
__accessCheck(obj, member, "read from private field");
return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd = (obj, member, value) => {
if (member.has(obj))
throw TypeError("Cannot add the same private member more than once");
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet = (obj, member, value, setter) => {
__accessCheck(obj, member, "write to private field");
setter ? setter.call(obj, value) : member.set(obj, value);
return value;
};
var __privateWrapper = (obj, member, setter, getter) => ({
set _(value) {
__privateSet(obj, member, value, setter);
},
get _() {
return __privateGet(obj, member, getter);
}
});
// node_modules/.pnpm/blueimp-md5@2.19.0/node_modules/blueimp-md5/js/md5.js
var require_md5 = __commonJS({
"node_modules/.pnpm/blueimp-md5@2.19.0/node_modules/blueimp-md5/js/md5.js"(exports, module) {
"use strict";
(function($) {
"use strict";
function safeAdd(x, y) {
var lsw = (x & 65535) + (y & 65535);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return msw << 16 | lsw & 65535;
}
function bitRotateLeft(num, cnt) {
return num << cnt | num >>> 32 - cnt;
}
function md5cmn(q, a, b, x, s, t) {
return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
}
function md5ff(a, b, c, d, x, s, t) {
return md5cmn(b & c | ~b & d, a, b, x, s, t);
}
function md5gg(a, b, c, d, x, s, t) {
return md5cmn(b & d | c & ~d, a, b, x, s, t);
}
function md5hh(a, b, c, d, x, s, t) {
return md5cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5ii(a, b, c, d, x, s, t) {
return md5cmn(c ^ (b | ~d), a, b, x, s, t);
}
function binlMD5(x, len) {
x[len >> 5] |= 128 << len % 32;
x[(len + 64 >>> 9 << 4) + 14] = len;
var i;
var olda;
var oldb;
var oldc;
var oldd;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for (i = 0; i < x.length; i += 16) {
olda = a;
oldb = b;
oldc = c;
oldd = d;
a = md5ff(a, b, c, d, x[i], 7, -680876936);
d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5gg(b, c, d, a, x[i], 20, -373897302);
a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5hh(d, a, b, c, x[i], 11, -358537222);
c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
a = md5ii(a, b, c, d, x[i], 6, -198630844);
d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
a = safeAdd(a, olda);
b = safeAdd(b, oldb);
c = safeAdd(c, oldc);
d = safeAdd(d, oldd);
}
return [a, b, c, d];
}
function binl2rstr(input) {
var i;
var output = "";
var length32 = input.length * 32;
for (i = 0; i < length32; i += 8) {
output += String.fromCharCode(input[i >> 5] >>> i % 32 & 255);
}
return output;
}
function rstr2binl(input) {
var i;
var output = [];
output[(input.length >> 2) - 1] = void 0;
for (i = 0; i < output.length; i += 1) {
output[i] = 0;
}
var length8 = input.length * 8;
for (i = 0; i < length8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 255) << i % 32;
}
return output;
}
function rstrMD5(s) {
return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
}
function rstrHMACMD5(key, data) {
var i;
var bkey = rstr2binl(key);
var ipad = [];
var opad = [];
var hash;
ipad[15] = opad[15] = void 0;
if (bkey.length > 16) {
bkey = binlMD5(bkey, key.length * 8);
}
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 909522486;
opad[i] = bkey[i] ^ 1549556828;
}
hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binlMD5(opad.concat(hash), 512 + 128));
}
function rstr2hex(input) {
var hexTab = "0123456789abcdef";
var output = "";
var x;
var i;
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i);
output += hexTab.charAt(x >>> 4 & 15) + hexTab.charAt(x & 15);
}
return output;
}
function str2rstrUTF8(input) {
return unescape(encodeURIComponent(input));
}
function rawMD5(s) {
return rstrMD5(str2rstrUTF8(s));
}
function hexMD5(s) {
return rstr2hex(rawMD5(s));
}
function rawHMACMD5(k, d) {
return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
}
function hexHMACMD5(k, d) {
return rstr2hex(rawHMACMD5(k, d));
}
function md52(string, key, raw) {
if (!key) {
if (!raw) {
return hexMD5(string);
}
return rawMD5(string);
}
if (!raw) {
return hexHMACMD5(key, string);
}
return rawHMACMD5(key, string);
}
if (typeof define === "function" && define.amd) {
define(function() {
return md52;
});
} else if (typeof module === "object" && module.exports) {
module.exports = md52;
} else {
$.md5 = md52;
}
})(exports);
}
});
// src/index.ts
var import_blueimp_md5 = __toESM(require_md5());
import { dirname, extname, relative, resolve } from "path";
import axios from "axios";
import { createWriteStream, emptyDir, ensureDir, existsSync, unlink } from "fs-extra";
import _debug from "debug";
import MagicString from "magic-string";
// node_modules/.pnpm/@antfu+utils@0.7.6/node_modules/@antfu/utils/dist/index.mjs
function slash(str) {
return str.replace(/\\/g, "/");
}
var Node = class {
constructor(value) {
__publicField(this, "value");
__publicField(this, "next");
this.value = value;
}
};
var _head, _tail, _size;
var Queue = class {
constructor() {
__privateAdd(this, _head, void 0);
__privateAdd(this, _tail, void 0);
__privateAdd(this, _size, void 0);
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (__privateGet(this, _head)) {
__privateGet(this, _tail).next = node;
__privateSet(this, _tail, node);
} else {
__privateSet(this, _head, node);
__privateSet(this, _tail, node);
}
__privateWrapper(this, _size)._++;
}
dequeue() {
const current = __privateGet(this, _head);
if (!current) {
return;
}
__privateSet(this, _head, __privateGet(this, _head).next);
__privateWrapper(this, _size)._--;
return current.value;
}
clear() {
__privateSet(this, _head, void 0);
__privateSet(this, _tail, void 0);
__privateSet(this, _size, 0);
}
get size() {
return __privateGet(this, _size);
}
*[Symbol.iterator]() {
let current = __privateGet(this, _head);
while (current) {
yield current.value;
current = current.next;
}
}
};
_head = new WeakMap();
_tail = new WeakMap();
_size = new WeakMap();
var VOID = Symbol("p-void");
// src/index.ts
var DefaultRules = [
{
match: /\b(https?:\/\/[\w_#&?.\/-]*?\.(?:png|jpe?g|svg|ico))(?=[`'")\]])/ig
}
];
function isValidHttpUrl(str) {
let url;
try {
url = new URL(str);
} catch (_) {
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}
function sleep(seconds) {
return new Promise((resolve2) => setTimeout(resolve2, seconds * 1e3));
}
var debug = _debug("vite-plugin-remote-assets");
function VitePluginRemoteAssets(options = {}) {
const {
assetsDir = "node_modules/.remote-assets",
rules = DefaultRules,
resolveMode = "relative",
awaitDownload = true,
retryTooManyRequests = false
} = options;
let dir = void 0;
let config;
let server;
async function downloadTo(url, filepath, { retryTooManyRequests: retryTooManyRequests2 }) {
const writer = createWriteStream(filepath);
const response = await axios({
url,
method: "GET",
validateStatus: (status) => {
if (status >= 200 && status < 300)
return true;
else if (retryTooManyRequests2 && status === 429)
return true;
else
return false;
},
responseType: "stream"
});
if (response.status === 429) {
const retryAfter = response.headers["retry-after"];
if (!retryAfter) {
throw new Error(`${url}: 429 without retry-after header`);
} else {
debug(`${url}: 429, retry after ${retryAfter} seconds`);
await sleep(retryAfter);
return await downloadTo(url, filepath, { retryTooManyRequests: retryTooManyRequests2 });
}
}
response.data.pipe(writer);
return new Promise((resolve2, reject) => {
writer.on("finish", resolve2);
writer.on("error", reject);
});
}
const tasksMap = {};
async function transform(code, id) {
const tasks = [];
const s = new MagicString(code);
let hasReplaced = false;
let match;
for (const rule of rules) {
rule.match.lastIndex = 0;
while (match = rule.match.exec(code)) {
const start = match.index;
const end = start + match[0].length;
const url = match[0];
if (!url || !isValidHttpUrl(url))
continue;
const hash = (0, import_blueimp_md5.default)(url) + (rule.ext || extname(url));
const filepath = slash(resolve(dir, hash));
debug("detected", url, hash);
if (!existsSync(filepath) || tasksMap[filepath]) {
if (!tasksMap[filepath]) {
tasksMap[filepath] = (async () => {
try {
debug("downloading", url);
await downloadTo(url, filepath, { retryTooManyRequests });
debug("downloaded", url);
} catch (e) {
if (existsSync(filepath))
await unlink(filepath);
throw e;
} finally {
delete tasksMap[filepath];
}
})();
}
tasks.push(tasksMap[filepath]);
if (!awaitDownload)
continue;
}
hasReplaced = true;
const mode = typeof resolveMode === "function" ? resolveMode(id, url) : resolveMode;
let newUrl;
if (mode === "relative") {
newUrl = slash(relative(dirname(id), `${dir}/${hash}`));
if (newUrl[0] !== ".")
newUrl = `./${newUrl}`;
} else {
let path = `${dir}/${hash}`;
if (!path.startsWith("/"))
path = `/${path}`;
newUrl = `/@fs${path}`;
}
s.overwrite(start, end, newUrl);
}
}
if (tasks.length) {
if (awaitDownload) {
await Promise.all(tasks);
} else {
Promise.all(tasks).then(() => {
if (server) {
const module = server.moduleGraph.getModuleById(id);
if (module)
server.moduleGraph.invalidateModule(module);
}
});
}
}
if (!hasReplaced)
return null;
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: true }) : null
};
}
return {
name: "vite-plugin-remote-assets",
enforce: "pre",
async configResolved(_config) {
config = _config;
dir = slash(resolve(config.root, assetsDir));
if (config.server.force)
await emptyDir(dir);
await ensureDir(dir);
},
configureServer(_server) {
server = _server;
},
async transform(code, id) {
return await transform(code, id);
},
transformIndexHtml: {
enforce: "pre",
async transform(code, ctx) {
var _a;
return (_a = await transform(code, ctx.filename)) == null ? void 0 : _a.code;
}
}
};
}
var src_default = VitePluginRemoteAssets;
export {
DefaultRules,
VitePluginRemoteAssets,
src_default as default
};
+70
View File
@@ -0,0 +1,70 @@
{
"name": "vite-plugin-remote-assets",
"type": "module",
"version": "0.6.0",
"packageManager": "pnpm@9.14.4",
"description": "Bundles your assets from remote urls with your app",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/antfu/vite-plugin-remote-assets#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/antfu/vite-plugin-remote-assets.git"
},
"bugs": {
"url": "https://github.com/antfu/vite-plugin-remote-assets/issues"
},
"keywords": [
"vite-plugin"
],
"sideEffects": false,
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"prepublishOnly": "nr build",
"dev": "nr build --watch",
"start": "esno src/index.ts",
"build": "tsup src/index.ts --format cjs,esm --dts",
"release": "bumpp --commit --push --tag && npm publish",
"lint": "eslint .",
"test": "vitest"
},
"peerDependencies": {
"vite": ">=5.0.0"
},
"dependencies": {
"@antfu/utils": "^0.7.10",
"axios": "^1.7.8",
"blueimp-md5": "^2.19.0",
"debug": "^4.3.7",
"fs-extra": "^11.2.0",
"magic-string": "^0.30.14"
},
"devDependencies": {
"@antfu/eslint-config": "^3.11.2",
"@antfu/ni": "^0.23.1",
"@types/blueimp-md5": "^2.18.2",
"@types/debug": "^4.1.12",
"@types/follow-redirects": "^1.14.4",
"@types/fs-extra": "^11.0.4",
"@types/node": "^22.10.1",
"bumpp": "^9.8.1",
"eslint": "^9.16.0",
"esno": "^4.8.0",
"tsup": "^8.3.5",
"typescript": "^5.7.2",
"vite": "^6.0.2",
"vitest": "^2.1.8"
}
}