Skip to content

Commit

Permalink
feat(@netlify/build-info): add React Router 7 detection
Browse files Browse the repository at this point in the history
Technically we'd want to add it to Vite's `excludedNpmDependencies`, but we actually only
want to exclude RR >=7. Many RR <7 users will be using Vite, so we don't want to break
that.

But everything works fine if we just don't do anything here, since frameworks get priority
over bundlers when there are multiple matches. (I'm not really sure why we both excluding
Vite frameworks in the Vite logic).
  • Loading branch information
serhalp committed Dec 10, 2024
1 parent 44527a0 commit 820bd41
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/build-info/src/frameworks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Parcel } from './parcel.js'
import { Phenomic } from './phenomic.js'
import { Quasar } from './quasar.js'
import { Qwik } from './qwik.js'
import { ReactRouter } from './react-router.js'
import { ReactStatic } from './react-static.js'
import { CreateReactApp } from './react.js'
import { RedwoodJS } from './redwoodjs.js'
Expand Down Expand Up @@ -65,6 +66,7 @@ export const frameworks = [
Nuxt,
Phenomic,
Qwik,
ReactRouter,
ReactStatic,
RedwoodJS,
Remix,
Expand Down
75 changes: 75 additions & 0 deletions packages/build-info/src/frameworks/react-router.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { beforeEach, expect, test } from 'vitest'

import { mockFileSystem } from '../../tests/mock-file-system.js'
import { NodeFS } from '../node/file-system.js'
import { Project } from '../project.js'

beforeEach((ctx) => {
ctx.fs = new NodeFS()
})
test('detects a React Router v7 site', async ({ fs }) => {
const cwd = mockFileSystem({
'react-router.config.ts': '',
'vite.config.ts': '',
'package.json': JSON.stringify({
scripts: {
build: 'react-router build',
dev: 'react-router dev',
start: 'react-router-serve ./build/server/index.js',
typecheck: 'react-router typegen && tsc',
},
dependencies: {
'@react-router/node': '^7.0.2',
'@react-router/serve': '^7.0.2',
react: '^18.3.1',
'react-dom': '^18.3.1',
'react-router': '^7.0.2',
},
devDependencies: {
'@netlify/react-router-vite-plugin': '^1.0.0',
'@react-router/dev': '^7.0.2',
typescript: '^5.6.3',
vite: '^5.4.11',
'vite-tsconfig-paths': '^5.1.2',
},
}),
})
const detected = await new Project(fs, cwd).detectFrameworks()

const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
expect(detectedFrameworks).not.toContain('remix')

expect(detected?.[0]?.id).toBe('react-router')
expect(detected?.[0]?.build?.command).toBe('react-router build')
expect(detected?.[0]?.build?.directory).toBe('build/client')
expect(detected?.[0]?.dev?.command).toBe('react-router dev')
expect(detected?.[0]?.dev?.port).toBe(5173)
})

test('does NOT detect a React Router <v7 site', async ({ fs }) => {
const cwd = mockFileSystem({
'vite.config.ts': '',
'package.json': JSON.stringify({
scripts: {
build: 'vite build',
dev: 'vite dev',
},
dependencies: {
react: '^18.2.0',
'react-dom': '^18.2.0',
'react-router-dom': '^6.15.0',
},
devDependencies: {
'@vitejs/plugin-react': '^3.0.1',
typescript: '^4.9.5',
vite: '^4.0.4',
},
}),
})
const detected = await new Project(fs, cwd).detectFrameworks()

const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
expect(detectedFrameworks).not.toContain('react-router')

expect(detected?.[0]?.id).toBe('vite')
})
40 changes: 40 additions & 0 deletions packages/build-info/src/frameworks/react-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { lt } from 'semver'

import { BaseFramework, Category, DetectedFramework, Framework } from './framework.js'

export class ReactRouter extends BaseFramework implements Framework {
readonly id = 'react-router'
name = 'React Router'
configFiles = ['react-router.config.ts', 'react-router.config.js']
npmDependencies = ['react-router']
category = Category.SSG

dev = {
port: 5173,
command: 'react-router dev',
}

build = {
command: 'react-router build',
directory: 'build/client',
}

logo = {
default: '/logos/react-router/light.svg',
light: '/logos/react-router/light.svg',
dark: '/logos/react-router/dark.svg',
}

async detect(): Promise<DetectedFramework | undefined> {
await super.detect()

if (this.detected) {
// React Router wasn't a framework before v7. As of v7, it's... Remix.
if (this.version && lt(this.version, '7.0.0')) {
return
}

return this as DetectedFramework
}
}
}

0 comments on commit 820bd41

Please sign in to comment.