Skip to content

Commit

Permalink
feat: Add plugin for React router 7 framework mode (#931)
Browse files Browse the repository at this point in the history
Co-authored-by: Lars Kappert <lars@webpro.nl>
  • Loading branch information
lasseklovstad and webpro authored Feb 10, 2025
1 parent 2afffe0 commit 012fd5b
Show file tree
Hide file tree
Showing 16 changed files with 120 additions and 0 deletions.
Empty file.
Empty file.
Empty file.
7 changes: 7 additions & 0 deletions packages/knip/fixtures/plugins/react-router/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default [
{ file: "routes/home.tsx", index: true },
{
file: "routes/layout.tsx",
children: [{ file: "./routes/another-route.tsx" }],
},
];
Empty file.
Empty file.
Empty file.
7 changes: 7 additions & 0 deletions packages/knip/fixtures/plugins/react-router/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@fixtures/react-router",
"version": "*",
"devDependencies": {
"@react-router/dev": "*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Config } from "@react-router/dev/config";

export default {
// Config options...
// Server-side render by default, to enable SPA mode set this to `false`
ssr: true,
} satisfies Config;
4 changes: 4 additions & 0 deletions packages/knip/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,10 @@
"title": "react-cosmos plugin configuration (https://knip.dev/reference/plugins/react-cosmos)",
"$ref": "#/definitions/plugin"
},
"react-router": {
"title": "react-router plugin configuration (https://knip.dev/reference/plugins/react-router)",
"$ref": "#/definitions/plugin"
},
"release-it": {
"title": "Release It plugin configuration (https://knip.dev/reference/plugins/release-it)",
"$ref": "#/definitions/plugin"
Expand Down
2 changes: 2 additions & 0 deletions packages/knip/src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { default as postcss } from './postcss/index.js';
import { default as preconstruct } from './preconstruct/index.js';
import { default as prettier } from './prettier/index.js';
import { default as reactCosmos } from './react-cosmos/index.js';
import { default as reactRouter } from './react-router/index.js';
import { default as releaseIt } from './release-it/index.js';
import { default as remark } from './remark/index.js';
import { default as remix } from './remix/index.js';
Expand Down Expand Up @@ -147,6 +148,7 @@ export const Plugins = {
preconstruct,
prettier,
'react-cosmos': reactCosmos,
'react-router': reactRouter,
'release-it': releaseIt,
remark,
remix,
Expand Down
61 changes: 61 additions & 0 deletions packages/knip/src/plugins/react-router/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { existsSync } from 'node:fs';
import type { IsPluginEnabled, Plugin, ResolveEntryPaths } from '../../types/config.js';
import { toEntry } from '../../util/input.js';
import { join } from '../../util/path.js';
import { hasDependency, load } from '../../util/plugin.js';
import vite from '../vite/index.js';
import type { PluginConfig, RouteConfigEntry } from './types.js';

// https://reactrouter.com/start/framework/routing

const title = 'react-router';

const enablers = ['@react-router/dev'];

const isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);

const config = ['react-router.config.{js,ts}', ...vite.config];

const resolveEntryPaths: ResolveEntryPaths<PluginConfig> = async (localConfig, options) => {
const { configFileDir } = options;
const appDirectory = localConfig.appDirectory ?? 'app';
const appDir = join(configFileDir, appDirectory);

// If using flatRoutes from @react-router/fs-routes it will throw an error if this variable is not defined
// @ts-ignore
globalThis.__reactRouterAppDirectory = appDir;

let routeConfig: RouteConfigEntry[] = [];

const routesPathTs = join(appDir, 'routes.ts');
const routesPathJs = join(appDir, 'routes.js');

if (existsSync(routesPathTs)) {
routeConfig = await load(routesPathTs);
} else if (existsSync(routesPathJs)) {
routeConfig = await load(routesPathJs);
}

const mapRoute = (route: RouteConfigEntry): string[] => {
return [join(appDir, route.file), ...(route.children ? route.children.flatMap(mapRoute) : [])];
};

const routes = routeConfig.flatMap(mapRoute);

return [
join(appDir, 'routes.{js,ts}'),
join(appDir, 'root.{jsx,tsx}'),
join(appDir, 'entry.{client,server}.{js,jsx,ts,tsx}'),
...routes,
].map(toEntry);
};

export default {
title,
enablers,
isEnabled,
config,
entry,
production,
resolveEntryPaths,
} satisfies Plugin;
8 changes: 8 additions & 0 deletions packages/knip/src/plugins/react-router/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type PluginConfig = {
appDirectory?: string;
};

export interface RouteConfigEntry {
file: string;
children?: RouteConfigEntry[];
}
1 change: 1 addition & 0 deletions packages/knip/src/schema/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const pluginsSchema = z.object({
preconstruct: pluginSchema,
prettier: pluginSchema,
'react-cosmos': pluginSchema,
'react-router': pluginSchema,
'release-it': pluginSchema,
remark: pluginSchema,
remix: pluginSchema,
Expand Down
2 changes: 2 additions & 0 deletions packages/knip/src/types/PluginNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type PluginName =
| 'preconstruct'
| 'prettier'
| 'react-cosmos'
| 'react-router'
| 'release-it'
| 'remark'
| 'remix'
Expand Down Expand Up @@ -148,6 +149,7 @@ export const pluginNames = [
'preconstruct',
'prettier',
'react-cosmos',
'react-router',
'release-it',
'remark',
'remix',
Expand Down
21 changes: 21 additions & 0 deletions packages/knip/test/plugins/react-router.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test } from 'bun:test';
import assert from 'node:assert/strict';
import { main } from '../../src/index.js';
import { resolve } from '../../src/util/path.js';
import baseArguments from '../helpers/baseArguments.js';
import baseCounters from '../helpers/baseCounters.js';

const cwd = resolve('fixtures/plugins/react-router');

test('Find dependencies with the react-router plugin', async () => {
const { counters } = await main({
...baseArguments,
cwd,
});

assert.deepEqual(counters, {
...baseCounters,
processed: 8,
total: 8,
});
});

0 comments on commit 012fd5b

Please sign in to comment.