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

vite-plugin-geo

v0.1.4

Published

Vite plugin to generate sitemap.xml, llms.txt, robots.txt, and optional GEO companion files.

Readme

🌍 vite-plugin-geo

一个面向 Vite 与 Astro 静态站点的构建后插件,自动生成 Sitemap、robots.txt、RSS、llms.txt 和页面 Markdown 镜像。

当前版本:[email protected]。支持 Vite 7 / 8,Node.js 要求 ^20.19.0 || >=22.12.0。Astro 7 是可选 Astro peer,纯 Vite 项目无需安装 Astro。

这些文件用于机器发现和抓取提示,不证明任何搜索引擎、AI 产品或模型会读取、采用、索引或优先展示内容,也不承诺排名、收录或转化效果。

✨ 核心特性

  • 🗺️ Sitemap — 自动生成 URL 集,超过 50,000 URL 或大小限制时自动拆分;兼容 Astro 官方 Sitemap
  • 🤖 robots.txt — 根路径生成、通用自定义指令、控制字符与 500 KiB 限制校验
  • 📰 RSS 2.0 — 自动注入 discovery link,包含 Atom self link 和稳定 GUID
  • 🧠 llms.txt v2 — 页面 Markdown 发现关系、单路径作用域,以及 append / replace 两种镜像命名
  • 📄 Markdown 镜像 — 保留已有有效 alternate,只为缺失页面生成镜像
  • 🌐 多语言支持 — Sitemap hreflang 映射
  • 👥 humans.txt — 可选生成,只输出可确认信息
  • 🛍️ SKU.md — 可选生成 1.0.0-beta 纯 Markdown 根清单
  • 🧱 统一行为 — Vite、Astro 和 CLI 共用同一套校验、规划与写入规则

📦 安装

npm i -D vite-plugin-geo
yarn add -D vite-plugin-geo
bun add -d vite-plugin-geo

🚀 快速开始

Vite 项目

// vite.config.js
import { defineConfig } from 'vite';
import geo from 'vite-plugin-geo';

export default defineConfig({
  plugins: [
    geo({
      domain: 'https://example.com',
    }),
  ],
});

domain 必须是完整的 HTTP(S) 站点源,只能包含协议、主机和可选端口,例如 https://example.com。不能包含账号、路径、查询参数或片段;子路径部署统一使用 routeBasePath 表达。

也可以把站点源写在项目 package.jsondomain 字段中;如果两处都配置,geo({ domain }) 优先。如果两处都没有配置,插件会停止生成,避免产出错误的公开链接。

Astro 项目

Astro 正式支持以下两种方式,二选一,不能同时配置。

方式一:Astro 原生集成

Astro 在内部 Vite 构建结束后才生成最终 HTML,因此不要把默认入口放入 vite.plugins。请使用 Astro 专用入口:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import geo from 'vite-plugin-geo/astro';

export default defineConfig({
  site: 'https://example.com',
  integrations: [geo()],
});

域名读取顺序为显式 geo({ domain })、Astro sitepackage.json.domain。通常已经配置 site 时无需重复填写 domain

方式二:构建后串联 CLI

{
  "domain": "https://example.com",
  "scripts": {
    "build": "astro build && vite-plugin-geo generate"
  }
}

CLI 默认扫描 dist/。如果域名只配置在 Astro site 中,CLI 不会读取它,需要追加 --domain https://example.com。自定义输出目录使用 --dist-dir <path>

默认构建结果:

dist/
├── sitemap.xml
├── robots.txt
├── feed.xml
├── llms.txt
├── index.html.md
└── ...其他页面.html.md

📄 输出文件

| 文件 | 默认 | 说明 | |---|---:|---| | sitemap.xml | ✅ | 50,000 URL 或配置大小超限时拆分并生成索引 | | robots.txt | 根作用域 ✅ | 非根 routeBasePath 默认关闭,可显式开启,但始终写到站点根目录 | | feed.xml | ✅ | RSS 2.0 订阅源,并向 HTML 注入 discovery link | | llms.txt | ✅ | 当前单一作用域的 llms.txt v2 内容索引 | | *.html.md / *.md | ✅ | 默认 append,可改为 replace 风格 | | humans.txt | ❌ | 可确认的团队、版权和技术信息 | | sku.md | ❌ | SKU.md 1.0.0-beta 根目录发现清单 | | sitemap_index.xml | 🔀 | Sitemap 超限时按配置自动生成 |

0.1.4 不再生成 llms-full.mdllms-ctx.txtllms-ctx-full.txt

⚙️ 配置项

| 选项 | 类型 | 默认值 | 说明 | |---|---|---|---| | domain | string | package.json.domain | 无凭据、路径、查询或片段的 HTTP(S) 站点源 | | distDir | string | Vite build.outDir | 构建输出目录 | | routeBasePath | string | Vite / Astro base | 单一公开路径作用域 | | prettyUrls | boolean | true | 页面公开 URL 是否移除 .html | | lastmodStrategy | 'none' \| 'file-mtime' \| 'build' \| 'git' | 'none' | Sitemap 与 RSS 条目时间来源 | | excludePatterns | string[] | ['404.html', '500.html'] | 排除页面规则 | | hreflang | Record<string, string> | — | Sitemap 多语言映射 | | generateSitemap | boolean | 自动 | 检测到已有 Sitemap 时默认关闭 | | sitemapUrl | string | 自动 | robots.txt / llms.txt 引用的 Sitemap 地址 | | llmsMdMirror | boolean | true | 是否为缺少 alternate 的页面生成 Markdown 镜像 | | llmsMdMirrorStyle | 'append' \| 'replace' | 'append' | about.html.mdabout.md | | llmsClassify | function | 自动 | 自定义 llms.txt 页面分区 | | llmsSections | Record<string, string \| string[]> | 自动 | 按路径片段配置 llms.txt 分区 | | generateRobots | boolean | 按作用域 | 根作用域默认开启,非根作用域默认关闭 | | robotsDirectives | string[] | [] | 通用 robots.txt 自定义指令 | | generateFeed | boolean | true | RSS 订阅源开关 | | generateHumans | boolean | false | humans.txt 开关 | | skuMd | SkuMdOptions | — | SKU.md 根清单配置 | | debug | boolean | false | 输出生成摘要 |

| 选项 | 类型 | 默认值 | 说明 | |---|---|---|---| | stripPathPrefixes | string[] | ['src/pages/'] | 计算公开 URL 时剥离的源码路径前缀 | | sourceHtmlDir | string | 'src/pages' | file-mtime 与 Git 时间解析使用的源码 HTML 目录 | | packageJsonPath | string \| false | './package.json' | 项目元数据路径;false 表示不读取 | | sitemapIndex | boolean | true | 是否允许自动拆分 Sitemap | | sitemapMaxBytes | number | 52428800 | 单个未压缩 Sitemap 的字节上限,不能超过 50 MiB | | skuMd.merchantName | string | 首页真实标题 | 商户名称;无法可靠推导时构建失败 | | skuMd.summary | string | 首页真实描述 | 商户摘要;无法可靠推导时构建失败 | | skuMd.catalog | { label; url }[] | 必填 | 至少一个同源 HTTPS 目录链接 |

旧配置键不会被忽略或兼容映射。llmsFullllmsCtxllmsCtxFullblockGoogleExtended、三个 stability* 选项,以及 SKU.md 的 catalogUrlcontentLanguagemarket 都会明确报错。

Astro 官方 Sitemap 兼容

如果 Astro 项目已经使用 @astrojs/sitemap,请把 sitemap() 放在 geo() 前面:

import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
import geo from 'vite-plugin-geo/astro';

export default defineConfig({
  site: 'https://example.com',
  integrations: [sitemap(), geo()],
});

插件在 Astro 的最终构建阶段检查真实 Sitemap 产物:

  • 跳过重复的 Sitemap 生成;
  • 继续生成 llms.txt、robots.txt、RSS 等已开启文件;
  • 让 robots.txt 和 llms.txt 指向实际发现的 Sitemap,包括自定义 filenameBase

需要手动覆盖时,可以使用:

geo({
  generateSitemap: true,
  sitemapUrl: 'sitemap.xml',
})

🔧 CLI 命令

除了 Vite 插件,也可以独立运行:

# 通过包管理器
npx vite-plugin-geo generate
bunx vite-plugin-geo generate

# 常用参数
vite-plugin-geo generate \
  --domain https://example.com \
  --dist-dir dist \
  --lastmod-strategy none \
  --llms-md-mirror-style append \
  --generate-robots

# 使用 JSON 配置文件
vite-plugin-geo generate --config ./geo.config.json

Astro 构建串联请参阅上方“Astro 项目”中的“方式二:构建后串联 CLI”。

generate 是默认命令。安装后可以通过 npxbunx、npm script 或 ./node_modules/.bin/vite-plugin-geo 运行。

| 参数 | 说明 | |---|---| | --domain <url> | 站点源,优先于 package.json.domain | | --dist-dir <path> | 构建输出目录,默认 dist | | --route-base-path <path> | URL 单一作用域前缀 | | --source-html-dir <path> | 源码 HTML 目录 | | --package-json-path <path> | package.json 路径;也支持 --package-json-path=false | | --strip-path-prefixes <list> | 逗号分隔的路径前缀 | | --exclude-patterns <list> | 逗号分隔的排除规则 | | --lastmod-strategy <value> | nonefile-mtimebuildgit | | --llms-md-mirror-style <value> | appendreplace | | --robots-directives <list> | 逗号分隔的通用 robots 指令 | | --sitemap-url <path-or-url> | robots.txt / llms.txt 引用的 Sitemap 地址 | | --pretty-urls / --no-pretty-urls | URL 模式开关 | | --sitemap-index / --no-sitemap-index | Sitemap 自动拆分开关 | | --generate-sitemap / --no-generate-sitemap | Sitemap 生成开关 | | --generate-robots / --no-generate-robots | robots.txt 生成开关 | | --llms-md-mirror / --no-llms-md-mirror | Markdown 镜像开关 | | --generate-feed / --no-generate-feed | RSS 开关 | | --generate-humans / --no-generate-humans | humans.txt 开关 | | --debug / --no-debug | 调试摘要开关 | | --config <path> | JSON 配置文件路径 |

被删除的旧参数会以 unknown argument 失败。

📝 行为契约

审核日期:2026-08-12。该日期表示 v0.1.4 的公开资料审核快照,不代表第三方规范以后不会变化。

| 文件或机制 | 状态 | v0.1.4 处理 | |---|---|---| | llms.txt v2 | 社区开放提案,不是 IETF/W3C 标准 | 单作用域索引、Markdown alternate 和 describedby 发现链接 | | robots.txt | IETF Standards Track,RFC 9309 | 根文件、通用指令、控制字符和 500 KiB 校验 | | Sitemap 0.9 | 行业协议,不是 IETF/W3C 标准 | URL 集、hreflang、自动拆分和索引 | | RSS 2.0.11 | 稳定公开规范,不是 IETF 标准 | RSS 2.0、Atom self link、稳定 GUID | | humans.txt | 社区约定 | 可选生成,仅包含可确认信息 | | SKU.md 1.0.0-beta | 实验性社区提案 | 可选生成根目录发现清单 |

🔗 URL 生成规则

| 模式 | 输入 | 输出 | |---|---|---| | prettyUrls: true | about.html | /about | | prettyUrls: true | docs/index.html | /docs | | prettyUrls: false | about.html | /about.html | | 首页 | index.html | /(始终) |

domain 只表示站点源;所有子路径由 routeBasePath 表达。插件先规划并校验全部目标路径,再统一写入。任何配置、路径或用户文件冲突都会在写入前停止,避免留下半成品。

🧠 llms.txt v2

实现依据:llms.txt proposal变更记录IANA Link Relation TypesRFC 8288 Web LinkingChrome Lighthouse llms.txt audit

llms.txt v2 是 2026-08-10 更新的社区提案,不是正式 Web 标准。v0.1.4 的约定如下:

  • 每个收录页面注入 rel="describedby" type="text/markdown",指向当前作用域的 llms.txt;
  • 缺少 Markdown alternate 时生成镜像,并注入 rel="alternate" type="text/markdown"
  • 已有有效 Markdown alternate 时保留原标签,在 llms.txt 中采用第一个有效地址,不重复生成镜像;
  • append 默认生成 about.html.md,replace 生成 about.md
  • routeBasePath: '/docs/' 生成 /docs/llms.txt,但不会自动创建更深层作用域;
  • noindex、404 / 500 和匹配 excludePatterns 的页面不注入、不收录;
  • 重复执行不追加重复标签,目标路径与手写文件冲突时构建停止。

提案没有标准版本字段,因此生成文件不会写 version: 2Optional 只是内容组织约定,没有机器可执行语义。llmsMdMirror: false 会关闭自动镜像;已有 alternate 仍会采用,没有 alternate 的页面则使用 HTML URL。

插件只负责静态 HTML 的 <link>。HTTP Link 响应头属于托管平台或服务器职责,插件不会模拟它。

🗺️ Sitemap

实现依据:Sitemaps protocolGoogle Sitemap 指南

  • 单个 Sitemap 最多 50,000 URL,未压缩大小最多 50 MB;超过限制时生成分片与 Sitemap index;
  • 默认 lastmodStrategy: 'none'。仅在显式选择 gitfile-mtimebuild 时尝试写入;选定来源无法提供可信单页时间时省略该字段;
  • 不生成 prioritychangefreq,Google 明确说明会忽略这两个值;
  • Sitemap 可以帮助发现 URL,但不保证抓取或索引。

🤖 robots 与 noindex 边界

实现依据:RFC 9309Google robots.txt 文档OpenAI crawler 说明

  • robots.txt 的公开位置始终是站点源的 /robots.txt,不能由子路径文件代替;
  • 根作用域默认生成,非根作用域默认关闭;即使显式开启也仍写到站点根目录;
  • 自定义指令含控制字符或生成内容超过 500 KiB 时,构建会失败;
  • robots.txt 是抓取提示,不是授权或安全机制,敏感资源仍需身份验证和服务端访问控制;
  • 页面过滤读取最终 HTML 中的 robots meta noindex;部署后添加的 X-Robots-Tag 无法在构建期识别。

OpenAI 将 OAI-SearchBot 用于搜索,GPTBot 用于可能进入基础模型训练的抓取,ChatGPT-User 用于用户触发的访问。三者用途及 robots.txt 适用方式不同;插件不内置厂商快捷开关,请用通用 robotsDirectives 分别配置。

📰 RSS Link 与时间

实现依据:RSS 2.0.11 specification

  • 生成 RSS 2.0,并向页面注入 application/rss+xml discovery link;
  • 已存在的 RSS 或 Atom discovery link 按属性语义识别并替换,重复执行不会注入多份;
  • Feed 包含 Atom self link,以页面规范 URL 作为稳定 GUID;
  • lastBuildDate 表示本次生成时间;没有可信页面时间时省略条目 pubDate

👥 humans.txt

实现依据:humans.txt convention。它是非正式社区约定。文件只包含明确配置或可从 package.json、构建过程可靠推导的信息,不写 TODO、示例联系人或猜测的技术栈。

🛍️ SKU.md 1.0.0-beta

实现依据:SKU.md 1.0.0-beta specificationchangelog。这是不兼容旧 YAML 草案的实验性纯 Markdown 格式。

  • 固定生成到站点根路径 /sku.md,不支持非根 routeBasePath
  • 包含 1.0.0-beta 版本行、一个商户标题与摘要、Website、Catalog,以及至少一个同源 HTTPS 目录链接;
  • 总大小不超过 65,536 UTF-8 字节,单行不超过 8,192 字节;
  • merchantNamesummary 可从首页真实元数据推导,否则构建失败;catalog 必须明确提供且非空;
  • 只声明目录入口,不生成商品知识文档,也不声明价格、库存、税费、配送、支付、交易或结账能力。

💡 常见配置

基础配置

geo({
  domain: 'https://example.com',
  distDir: 'dist',
  packageJsonPath: './package.json',
})

子路径与 replace 镜像

geo({
  domain: 'https://example.com',
  routeBasePath: '/docs/',
  llmsMdMirrorStyle: 'replace',
  generateRobots: false,
})

此配置生成 /docs/llms.txtabout.md 风格镜像。非根作用域默认不生成 robots.txt;示例显式写出该选择,便于审阅。

多语言站点

geo({
  domain: 'https://example.com',
  hreflang: {
    en: 'https://example.com/{path}',
    'zh-CN': 'https://example.com/zh/{path}',
    'x-default': 'https://example.com/{path}',
  },
})

SKU.md 商户根清单

geo({
  domain: 'https://shop.example.com',
  skuMd: {
    merchantName: 'Example Store',
    summary: 'Independent tea catalog.',
    catalog: [
      {
        label: 'All products',
        url: 'https://shop.example.com/collections/all',
      },
    ],
  },
})

如果省略 merchantNamesummary,插件只会采用首页真实 title 和 description;无法可靠推导时停止构建,不会编造内容。

🔭 已审核但不生成

| 提案或机制 | 审核结论 | |---|---| | IETF AIPREF | 截至审核日,词汇表仍是 draft-ietf-aipref-vocab-06,HTTP 关联草案已过期;尚未成为 RFC | | Cloudflare Content Signals | 平台特定实现;content-use 仍标为测试功能,不是通用 IETF Content-Usage 标准 | | W3C TDM Reservation Protocol | W3C Community Group Final Report,不是 W3C Recommendation | | ai.txt | 个别 Internet-Draft / 社区方案,没有稳定的通用部署约定 | | Shopify agents.md | Shopify 店面约定,不应扩展为所有 Vite 网站的默认文件 | | WebMCP Draft Community Group Report | 浏览器运行时工具 API,不是静态规范文件,也不是 W3C 标准或标准轨道文档 |

v0.1.4 不会生成以上文件,也不会把它们映射成 robots.txt 或 llms.txt 的别名。

🆕 0.1.4 发布说明

  • 完整实现 llms.txt v2 的 HTML 发现关系、单路径作用域和 append / replace 镜像;
  • 删除三类旧 llms 扩展文件与对应配置;
  • Sitemap 默认不写 lastmod,并删除 prioritychangefreq
  • 新增独立 generateRobots 开关,非根作用域默认不生成 robots.txt;
  • RSS 增加 Atom self link 和稳定 GUID,不再编造条目时间;
  • SKU.md 从旧 YAML 草案切换到 1.0.0-beta 纯 Markdown 根清单;
  • 删除 blockGoogleExtended、三个 stability* 选项及旧 SKU.md 配置,不提供兼容层。

发布前应在本地完成完整测试、覆盖率、消费者矩阵、注册表元数据、打包清单、依赖安全和差异格式检查。发布、推送和部署必须另行取得明确批准。

默认导出和现有命名导出均可使用:

import geo, { createGeoGeneratorPlugin } from 'vite-plugin-geo';