@edgeone/is-spa
v0.0.3
Published
Detect whether a frontend project is SPA, MPA, Hybrid, or SSG. Determine if Nginx/CDN fallback to index.html is needed.
Readme
is-spa-project
检测前端项目是否为 SPA(单页应用),判断部署时是否需要配置 Nginx/CDN 回源兜底到 index.html。
支持源码检测和构建产物检测两种模式,覆盖 React、Vue、Angular、Svelte、Next.js、Nuxt、SvelteKit、Remix、React Router 等主流框架。
快速开始
const { needsFallback } = require('is-spa-project')
// 传入任意目录,自动判断
needsFallback('./my-project') // true — 需要配置兜底
needsFallback('./my-project/dist') // true
needsFallback('./my-nuxt-app') // false — 不需要兜底安装
npm install is-spa-projectAPI
needsFallback(dir, options?): boolean
推荐使用。 判断是否需要配置回源兜底。
const { needsFallback } = require('is-spa-project')
// 自动模式(默认)— 自动判断源码/产物
needsFallback('./cra-spa') // true
needsFallback('./cra-spa/build') // true
needsFallback('./nuxt-app') // false
// 强制源码检测
needsFallback('./my-project', { mode: 'source' })
// 强制构建产物检测
needsFallback('./my-project/dist', { mode: 'dist' })options.mode:
| 值 | 说明 |
|---|---|
| 'auto'(默认) | 自动判断目录是源码还是构建产物 |
| 'source' | 强制源码检测(分析 package.json、配置文件) |
| 'dist' | 强制构建产物检测(分析 HTML 和 JS 文件) |
autoDetect(dir): DetectionResult
自动识别目录类型,返回完整检测结果。
const { autoDetect } = require('is-spa-project')
const result = autoDetect('./my-project')
console.log(result.mode) // 'source' | 'dist'
console.log(result.type) // 'spa' | 'mpa' | 'hybrid' | 'ssg' | 'unknown'
console.log(result.confidence) // 'high' | 'medium' | 'low'
console.log(result.reason) // 人类可读的判定原因detect(projectRoot): DetectionResult
源码检测模式。分析 package.json、配置文件、入口文件。
detectBuildOutput(distDir): DetectionResult
构建产物检测模式。分析 HTML 文件和 JS bundle 中的路由代码。
isSpa(projectRoot): boolean
源码检测简化版,等价于 detect().type === 'spa' || 'hybrid'。
CLI
# 自动模式(自动判断源码/产物)
npx is-spa ./my-project
npx is-spa ./my-project/dist
# 强制构建产物模式
npx is-spa ./my-project/dist --dist
# JSON 输出
npx is-spa ./my-project --json
npx is-spa ./my-project/dist --dist --json检测结果类型
| 类型 | 含义 | 需要兜底? | 部署配置 |
|------|------|-----------|---------|
| spa | 构建产物只有单个 index.html | ✅ 需要 | try_files $uri $uri/ /index.html |
| hybrid | SPA 主体 + 独立辅助页面 | ✅ 需要 | 主入口需要兜底 |
| mpa | 多个独立 HTML 入口 | ❌ 不需要 | 每个路由有对应文件 |
| ssg | SSR/SSG 框架,每个路由有对应 HTML | ❌ 不需要 | 框架构建工具自动处理 |
| unknown | 无法判定 | ❌ 不需要 | — |
支持的框架和路由库
框架检测(快速路径)
| 框架 | 依赖 | 默认判定 | 可切换为 SPA? |
|------|------|---------|-------------|
| Create React App | react-scripts | spa | — |
| Angular CLI | @angular/cli | spa | — |
| Next.js | next | ssg | ❌ |
| Nuxt | nuxt | ssg | ❌(generate 自动处理) |
| Gatsby | gatsby | ssg | ❌ |
| React Router (Framework) | @react-router/dev | ssg | ✅ ssr: false → spa |
| SvelteKit | @sveltejs/kit | ssg | ✅ fallback → spa |
| Remix | @remix-run/react | ssg | ✅ ssr: false → spa |
路由库检测
| 路由库 | 生态 |
|--------|------|
| vue-router | Vue |
| react-router-dom / react-router | React |
| @tanstack/react-router | React |
| wouter | React |
| @angular/router | Angular |
| svelte-routing | Svelte |
| svelte-spa-router | Svelte |
| page (page.js) | Vanilla JS |
| navigo | Vanilla JS |
| universal-router | Vanilla JS |
检测原理
源码检测
package.json → 匹配已知框架?→ 框架快速路径(可能读取配置文件)
↓ 否
检测路由库依赖 + 入口文件数量 + router 使用 → 综合判定构建产物检测
根目录有 index.html?
├── 没有 → unknown(不存在回源目标)
└── 有 → 扫描 HTML + JS → 判定| 信号 | SPA | SSG | MPA | |------|-----|-----|-----| | 应用 HTML 数 | 1 | 多个 | 多个 | | HTML body | 空壳 | 有预渲染内容 | 有各自内容 | | JS 含路由代码 | ✅ | ✅ | ❌ | | 多 HTML 共享 JS | — | ✅ | ❌ |
needsFallback 自动检测
传入目录 → 判断是源码还是产物
├── 有 package.json(含 dependencies)或 src/ → 源码检测
└── 否则 → 产物检测
主模式结果 unknown(low) → 回退另一种模式(仅采纳 high 置信度)测试
npm test # 源码检测(24 个测试项目)
npm run test:dist # 构建产物检测(24 个测试项目)
npm run test:all # 全部运行测试覆盖 24 个项目:CRA、Vite+React、Vite+Vue、Angular、Vue CLI(SPA/MPA/Hybrid)、Next.js、Nuxt(SSG/SPA)、SvelteKit(SSG/SPA)、Remix(SSR/SPA)、React Router Framework(SPA)、TanStack Router、wouter、svelte-routing、svelte-spa-router、page.js、navigo、universal-router、原生 HTML(单页/多页)。
License
MIT
