npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@resource-fallback/vite-plugin

v0.3.1

Published

Vite 4+ plugin that wires @resource-fallback/core into your build (HTML injection + dynamic import rewriting)

Readme

@resource-fallback/vite-plugin

中文 | English

Vite 4+ 插件,为 Vite 构建产物(同步 JS/CSS、异步 chunk)提供运行时重试与多 CDN 回退;对 modulepreload 失败则提供受控的错误阻止与规则闸门,不直接替换 preload URL。

安装

pnpm add @resource-fallback/vite-plugin -D

基本用法

// vite.config.ts
import { defineConfig } from 'vite';
import resourceFallback from '@resource-fallback/vite-plugin';

export default defineConfig({
  base: 'https://cdn.example.com/',
  plugins: [
    resourceFallback({
      rules: [
        {
          base: 'https://cdn.example.com/',
          urls: [
            'https://cdn-backup.example.com/',
            '/', // 回源
          ],
        },
      ],
    }),
  ],
});

重要:Vite base 应当与 rules[].base(rule base)保持一致,确保构建产物的 URL 能被规则匹配。仅当两边在规范化尾斜杠后相等(ensureTrailingSlash(viteBase) === ensureTrailingSlash(r.base))时才会改写 URL。

工作原理

插件在构建时按下面的顺序工作:

1. configResolved:比较规范化后的 Vite base 与 rule base

插件先在 configResolved 阶段读取 Vite 最终解析后的 base,再和每条 rules[].base 做同样的尾斜杠规范化比较。

  • 如果规范化后的 Vite base 至少命中一条 rule base,后续才继续启用 URL 改写
  • 如果没有命中,writeBundle 会跳过动态 import 改写,避免把非 CDN 构建误写成外域地址

2. generateBundle:按需发出 Service Worker 资源

如果开启了 serviceWorker,插件会在 generateBundle 阶段按需发出:

  • rf-sw.js
  • manifest.json

3. writeBundle:解析字面量动态 import,并替换为 window.__RF__.load(filename)

writeBundle 会先用 es-module-lexer 解析 chunk 里的动态 import,再只改写满足以下条件的字面量导入:

  1. 该 import 是字面量字符串
  2. 解析出的文件名对应 chunk.dynamicImports 里的条目
  3. 当前构建已经通过规范化 base 门禁

匹配到的 import() 会被替换成 window.__RF__.load(filename),例如:

// 原始代码
const mod = await import('./Lazy.vue');

// 构建后
const mod = await window.__RF__.load('assets/Lazy-abc.js');

4. transformIndexHtml:注入运行时与 preconnect 标签

transformIndexHtml 会在 <head> 注入:

  • <link rel="preconnect"> 标签
  • 内联运行时 IIFE 和 install(config) 调用的 <script>

RF.url 行为

__RF__.url(filename) 只会把文件名和第一条编译后的 rule base 拼接起来,它不会查看熔断状态,也不会在运行时跳过 host。

RF.load 回退循环

__RF__.load(filename) 会先解析初始 URL,然后把 retry / fallback / deadline / cancellation 都交给共享的 Coordinator 处理。它还会使用规范化后的 URL key,这样同一 owner 的并发加载可以共享同一个 recovery Promise。

vite:preloadError 处理

运行时还监听 Vite 的 vite:preloadError 事件。当 modulepreload 失败时:

  • 读取 event.payload
  • 提取出的 URL 若匹配已配置的 rule,就调用 event.preventDefault()
  • 不会直接更新熔断器状态、发出恢复事件,也不会自己选择 fallback URL
  • CSS <link> 失败仍然由 Observer 负责

配置

ViteResourceFallbackOptions 等同于 @resource-fallback/core 的 PluginOptions,完整字段参见根目录 README。

常用配置示例

resourceFallback({
  rules: [
    {
      base: 'https://cdn.example.com/',
      urls: ['https://cdn-backup.example.com/', 'https://static.mysite.com/', '/'],
      retry: { max: 2, baseDelay: 300, maxDelay: 3000, jitter: true },
      circuit: { threshold: 3, cooldown: 30000 },
    },
  ],
  debug: 'auto', // localStorage.__RF_DEBUG__ 控制日志
  sri: 'strip', // fallback 时移除 integrity
  nonce: 'my-csp-nonce', // CSP nonce
  injectPreconnect: true, // 注入 <link rel="preconnect">
  htmlInject: 'head-prepend', // 注入到 <head> 最前面
});

与 @vitejs/plugin-legacy 配合

若项目使用 @vitejs/plugin-legacy 生成 SystemJS 格式的 legacy bundle,运行时会自动安装 SystemJS adapter,通过 hook System.constructor.prototype.instantiate 为 legacy 入口和异步 chunk 提供回退能力。

// vite.config.ts
import legacy from '@vitejs/plugin-legacy';
import resourceFallback from '@resource-fallback/vite-plugin';

export default defineConfig({
  base: 'https://cdn.example.com/',
  plugins: [
    legacy({ targets: ['defaults', 'not IE 11'] }),
    resourceFallback({
      rules: [{ base: 'https://cdn.example.com/', urls: ['/'] }],
    }),
  ],
});

Vite Dev 模式

默认情况下插件在 dev 模式不激活(enableDev: false)。Vite dev server 使用原生 ESM,动态 import 失败无法拦截。

vite build && vite preview

设置 enableDev: true 会在 dev 模式下也注入运行时,但仅同步 <script> / <link> 的 error 事件有效。

同步/异步覆盖

| 场景 | Vite (build/preview) | Vite (dev) | | -------------------------- | -------------------------------------------- | ---------- | | 同步 <script> / <link> | ✓ Observer | ✓ Observer | | 异步 chunk(import()) | ✓ __RF__.load + writeBundle 改写 | ✗ | | CSS 动态注入 | ✓ Observer | ✓ Observer | | SystemJS(legacy bundle) | ✓ instantiate hook | — | | 图片 / 字体 / 媒体资源 | ✓ Hybrid SW(opt-in) | ✗ | | CSS url() / @font-face | ✓ Hybrid SW(opt-in) | ✗ | | CSS @import | ✓ Hybrid SW(需 CSS referrer 命中 manifest) | ✗ |

许可证

MIT