Skip to content

Instantly share code, notes, and snippets.

@rhyek
Last active September 21, 2024 21:00
Show Gist options
  • Save rhyek/9f18008e88563b27c5feb252fcd3e185 to your computer and use it in GitHub Desktop.
Save rhyek/9f18008e88563b27c5feb252fcd3e185 to your computer and use it in GitHub Desktop.
Next.js 13 App Router file-based routing in Remix (with Vite)
import { existsSync } from 'node:fs';
import path from 'node:path';
import { vitePlugin as remix } from '@remix-run/dev';
import { DefineRoutesFunction } from '@remix-run/dev/dist/config/routes';
import * as glob from 'glob';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [
remix({
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
},
ignoredRouteFiles: ['**/*'],
routes() {
const manifest: ReturnType<DefineRoutesFunction> = {};
const appFolder = path.resolve(__dirname, 'app');
const files = globSync('routes/**/{layout,page,route}.{tsx,ts}', {
cwd: appFolder,
}).sort();
for (const file of files) {
let currentPath = file;
let foundLayout: string | null = null;
while (currentPath && currentPath !== 'routes') {
currentPath = path.dirname(currentPath);
const possibleLayouts = [
path.join(currentPath, 'layout.ts'),
path.join(currentPath, 'layout.tsx'),
];
for (const possibleLayout of possibleLayouts) {
if (
file !== possibleLayout &&
existsSync(path.resolve(appFolder, possibleLayout))
) {
foundLayout = possibleLayout;
break;
}
}
if (foundLayout) {
break;
}
}
const id = path.join(path.dirname(file), path.parse(file).name);
const parentId = foundLayout
? path.join(path.dirname(foundLayout), path.parse(foundLayout).name)
: 'root';
const diff = (
parentId === 'root'
? path.dirname(id)
: path.dirname(id).split(path.dirname(parentId))[1]
)?.replace(/^routes/, '');
const _path =
diff
?.split('/')
.map((part) => {
if (part.startsWith('$')) {
return `:${part.slice(1)}`;
}
const bracketedMatch = part.match(/^\[(.+)\]$/);
if (bracketedMatch) {
return `:${bracketedMatch[1]}`;
}
if (part.startsWith('_') || part.match(/^\(.+\)$/)) {
return null;
}
return part;
})
.filter(Boolean)
.join('/') || undefined;
const index =
['page', 'route'].includes(path.parse(file).name) &&
files.some(
(f) =>
path.dirname(f) === path.dirname(file) &&
path.parse(f).name === 'layout',
);
manifest[id] = {
id,
file,
path: index
? undefined
: _path
? _path
: path.parse(file).name === 'layout'
? '/'
: undefined,
parentId,
index,
};
}
return manifest;
},
}),
tsconfigPaths(),
],
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment