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/webpack-plugin

v0.0.4

Published

Webpack 5 plugin that wires @resource-fallback/core into your build (HTML injection + chunkLoadingGlobal forwarding)

Readme

@resource-fallback/webpack-plugin

Webpack 5+ 插件,为 Webpack 构建产物(入口脚本、异步 chunk、CSS)提供运行时重试与多 CDN 回退能力。

安装

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

需要同时安装 html-webpack-plugin(v4+)以自动注入运行时。

基本用法

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ResourceFallbackWebpackPlugin } = require('@resource-fallback/webpack-plugin');

module.exports = {
  output: {
    publicPath: 'https://cdn.example.com/',
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new ResourceFallbackWebpackPlugin({
      rules: [
        {
          match: 'https://cdn.example.com/',
          urls: [
            'https://cdn-backup.example.com/',
            '/',  // 回源
          ],
        },
      ],
    }),
  ],
};

重要output.publicPath 应当与 match 保持一致。

工作原理

插件在构建时完成两件事,运行时提供双层保护:

构建时

1. HTML 注入

通过 html-webpack-pluginalterAssetTagGroups 钩子在 <head> 中注入:

  • <link rel="preconnect"> 标签(为每个 fallback 域名预建连接)
  • <script> 内联运行时 IIFE + install(config) 调用

如果未检测到 html-webpack-plugin,插件会输出警告,不会自动注入。此时需要通过 @resource-fallback/coregetRuntimeCode() 手动注入。

2. RuntimeModule 注入

注入一个 Webpack RuntimeModule(stage = STAGE_TRIGGER),在 webpack 的 bootstrap 内部 patch __webpack_require__.l——在其定义之后、首次 chunk 加载触发之前。这比从外部 monkey-patch 可靠得多。

运行时 — 双层保护

第一层:__webpack_require__.l 包装

webpack 所有异步 chunk(包括 React.lazy()、动态 import())都通过 __webpack_require__.l 加载 <script>。包装后的流程:

chunk 加载请求
  │
  ├── __webpack_require__.l(url, done, key, chunkId)
  │   │
  │   ├── 原始 <script> 加载
  │   │   ├── 成功 → recordSuccess → done(event)
  │   │   └── 失败 → resolver.resolve()
  │   │       ├── retry → 创建新 <script>,延迟重试
  │   │       ├── fallback → 创建新 <script>,切换 URL
  │   │       └── giveup → done(event)(让 webpack 处理错误)

每次重试/回退都创建全新的 <script> 元素(设置 data-webpack 属性),绕过浏览器缓存。

第二层:Observer

Observer 作为安全网,处理 __webpack_require__.l 未覆盖的场景:

  • 入口脚本(无 data-webpack 属性)
  • CSS chunkmini-css-extract-plugin 输出的 <link> 标签,虽然也带 data-webpack,但 webpack adapter 不处理 CSS)
  • 其他外部 <script>

Observer 自动跳过带 data-webpack 属性的 <script> 标签,避免与 webpack adapter 重复处理。

chunkLoadingGlobal hook

运行时还会 hook window[chunkLoadingGlobal](默认 webpackChunk_)的 push 方法。当 webpack bootstrap 安装 __webpack_require__ 后,运行时会捕获并包装 __webpack_require__.l。这提供了一个备用路径:即使 RuntimeModule 因某些原因未生效,外部 hook 也能接管。

配置

WebpackPluginOptions 等同于 @resource-fallback/corePluginOptions,完整字段参见根目录 README

常用配置示例

new ResourceFallbackWebpackPlugin({
  rules: [
    {
      match: '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',
  sri: 'strip',
  nonce: 'my-csp-nonce',
  injectPreconnect: true,
})

注意事项

非浏览器 target

targetnode / webworker / electron-main 时,插件自动跳过,不注入任何内容。

React.lazy 错误处理

使用 React.lazy() 时,如果异步 chunk 在所有候选 URL 耗尽后仍然失败,React.lazy() 会抛出错误。<Suspense> 只处理 loading 状态,不处理错误。建议包裹 ErrorBoundary

class ChunkErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { error: Error | null }
> {
  state = { error: null };

  static getDerivedStateFromError(error: Error) {
    return { error };
  }

  render() {
    if (this.state.error) {
      return <div>资源加载失败,请刷新页面重试</div>;
    }
    return this.props.children;
  }
}

// 使用
<ChunkErrorBoundary>
  <Suspense fallback={<Loading />}>
    <LazyComponent />
  </Suspense>
</ChunkErrorBoundary>

入口脚本兜底

入口脚本(entry bundle)如果所有 fallback 都失败,React/Vue 不会初始化,页面白屏。建议在 index.html 中添加内联的 rf:error 监听:

<script>
  window.addEventListener('rf:error', function() {
    document.body.innerHTML = '<p>资源加载失败,请刷新页面</p>';
  });
</script>

许可证

MIT