-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@netlify/build-info): add React Router 7 detection
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
Showing
3 changed files
with
117 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} |