├── .gitignore ├── .eslintrc ├── public └── favicon.ico ├── remix.env.d.ts ├── app ├── lib │ └── test.server.ts ├── entry.client.tsx ├── services.server │ └── service.ts ├── entry.server.tsx ├── root.tsx └── routes │ └── index.tsx ├── remix.config.js ├── tsconfig.json ├── README.md ├── package.json └── patches └── @remix-run+dev+1.6.5.patch /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /.cache 4 | /build 5 | /public/build 6 | .env 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node"] 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiliman/remix-server-folders/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /remix.env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /app/lib/test.server.ts: -------------------------------------------------------------------------------- 1 | console.log("test.server.ts", "Only run on server"); 2 | 3 | export function testServer() { 4 | return "test server"; 5 | } 6 | -------------------------------------------------------------------------------- /app/entry.client.tsx: -------------------------------------------------------------------------------- 1 | import { RemixBrowser } from "@remix-run/react"; 2 | import { hydrate } from "react-dom"; 3 | 4 | hydrate(, document); 5 | -------------------------------------------------------------------------------- /app/services.server/service.ts: -------------------------------------------------------------------------------- 1 | console.log("service.ts", "Only run on server"); 2 | export function serviceOnSever() { 3 | return "service on server"; 4 | } 5 | -------------------------------------------------------------------------------- /remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | // appDirectory: "app", 5 | // assetsBuildDirectory: "public/build", 6 | // serverBuildPath: "build/index.js", 7 | // publicPath: "/build/", 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], 3 | "compilerOptions": { 4 | "lib": ["DOM", "DOM.Iterable", "ES2019"], 5 | "isolatedModules": true, 6 | "esModuleInterop": true, 7 | "jsx": "react-jsx", 8 | "moduleResolution": "node", 9 | "resolveJsonModule": true, 10 | "target": "ES2019", 11 | "strict": true, 12 | "allowJs": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "baseUrl": ".", 15 | "paths": { 16 | "~/*": ["./app/*"] 17 | }, 18 | 19 | // Remix takes care of building everything in `remix build`. 20 | "noEmit": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/entry.server.tsx: -------------------------------------------------------------------------------- 1 | import type { EntryContext } from "@remix-run/node"; 2 | import { RemixServer } from "@remix-run/react"; 3 | import { renderToString } from "react-dom/server"; 4 | 5 | export default function handleRequest( 6 | request: Request, 7 | responseStatusCode: number, 8 | responseHeaders: Headers, 9 | remixContext: EntryContext 10 | ) { 11 | let markup = renderToString( 12 | 13 | ); 14 | 15 | responseHeaders.set("Content-Type", "text/html"); 16 | 17 | return new Response("" + markup, { 18 | status: responseStatusCode, 19 | headers: responseHeaders, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /app/root.tsx: -------------------------------------------------------------------------------- 1 | import type { MetaFunction } from "@remix-run/node"; 2 | import { 3 | Links, 4 | LiveReload, 5 | Meta, 6 | Outlet, 7 | Scripts, 8 | ScrollRestoration, 9 | } from "@remix-run/react"; 10 | 11 | export const meta: MetaFunction = () => ({ 12 | charset: "utf-8", 13 | title: "New Remix App", 14 | viewport: "width=device-width,initial-scale=1", 15 | }); 16 | 17 | export default function App() { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📦 remix-server-folders 2 | 3 | This patch updates the Remix compiler to treat all files in any folder named with `.server` extension as _server only_ files. In other words, it acts like you named all the files with `*.server.ts` extension. 4 | 5 | This just makes it easier to add files to the **server** folder without having to change the name. 6 | 7 | See example. Both methods work. 8 | 9 | ``` 10 | ├── lib 11 | │ └── test.server.ts 12 | └── services.server 13 | └── service.ts 14 | ``` 15 | 16 | To install, add `patch-package` to your project, then copy the _patch_ file for your specific Remix version to the `/patches` folder in your project. Also add `"postinstall": "patch-package"` to _package.json_ 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "sideEffects": false, 4 | "scripts": { 5 | "postinstall": "patch-package", 6 | "build": "remix build", 7 | "dev": "remix dev", 8 | "start": "remix-serve build" 9 | }, 10 | "dependencies": { 11 | "@remix-run/node": "^1.6.5", 12 | "@remix-run/react": "^1.6.5", 13 | "@remix-run/serve": "^1.6.5", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2" 16 | }, 17 | "devDependencies": { 18 | "@remix-run/dev": "^1.6.5", 19 | "@remix-run/eslint-config": "^1.6.5", 20 | "@types/react": "^17.0.47", 21 | "@types/react-dom": "^17.0.17", 22 | "eslint": "^8.20.0", 23 | "patch-package": "^6.4.7", 24 | "typescript": "^4.7.4" 25 | }, 26 | "engines": { 27 | "node": ">=14" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { json, type LoaderArgs } from "@remix-run/node"; 2 | import { useLoaderData } from "@remix-run/react"; 3 | import { testServer } from "~/lib/test.server"; 4 | import { serviceOnSever } from "~/services.server/service"; 5 | 6 | // uncomment this code and it'll break on client render 7 | //const test = testServer(); 8 | //const service = serviceOnSever(); 9 | 10 | export const loader = async ({ request }: LoaderArgs) => { 11 | const test = testServer(); 12 | const service = serviceOnSever(); 13 | return json({ test, service }); 14 | }; 15 | 16 | export default function Index() { 17 | const data = useLoaderData(); 18 | return ( 19 |
20 |

Welcome to Remix

21 |
{JSON.stringify(data, null, 2)}
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /patches/@remix-run+dev+1.6.5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@remix-run/dev/dist/compiler.js b/node_modules/@remix-run/dev/dist/compiler.js 2 | index 9d39471..5c0783a 100644 3 | --- a/node_modules/@remix-run/dev/dist/compiler.js 4 | +++ b/node_modules/@remix-run/dev/dist/compiler.js 5 | @@ -314,7 +314,7 @@ async function createBrowserBuild(config, options) { 6 | entryPoints[id] = path__namespace.resolve(config.appDirectory, config.routes[id].file) + "?browser"; 7 | } 8 | 9 | - let plugins = [urlImportsPlugin.urlImportsPlugin(), mdx.mdxPlugin(config), browserRouteModulesPlugin.browserRouteModulesPlugin(config, /\?browser$/), emptyModulesPlugin.emptyModulesPlugin(config, /\.server(\.[jt]sx?)?$/), nodeModulesPolyfill.NodeModulesPolyfillPlugin(), esbuildPluginPnp.pnpPlugin()]; 10 | + let plugins = [urlImportsPlugin.urlImportsPlugin(), mdx.mdxPlugin(config), browserRouteModulesPlugin.browserRouteModulesPlugin(config, /\?browser$/), emptyModulesPlugin.emptyModulesPlugin(config, /(\.server(\.[jt]sx?)?$)|(\/\w*\.server\/)/), nodeModulesPolyfill.NodeModulesPolyfillPlugin(), esbuildPluginPnp.pnpPlugin()]; 11 | return esbuild__namespace.build({ 12 | entryPoints, 13 | outdir: config.assetsBuildDirectory, 14 | --------------------------------------------------------------------------------