blues-stack
blues-stack copied to clipboard
chore: add nx to the repo
This PR is a follow-up of my conversation with Kent.
This PR shows how to add Nx to this repo, make some operations faster, and improve some dev ergonomics.
- Everything but "npm run dev" is cacheable now, so if you run "npm run build" twice, the second time will always be free.
- "npm run dev"'s output is nicer (see the video below).
- This PR enables Nx Cloud, which provides distributed caching and nicer UI for the invoked commands (see the video below). This is optional. Nx Cloud GitHub app can be enabled to post a comment to every PR with the links to the commands the PR ran.
Watch this video to see how everything works now: LINK
I intentionally didn't change anything about the repo itself to make it less overwhelming. So I want to mention that this isn't the best way to use Nx (or any such tool really). For instance, cypress and prisma can be made separate from app, which would make the dev ergonomics nicer etc.
This repo is also small, so both DX and performance advantages Nx offers aren't really that important here. The build command is fast enough without caching and distribution, and the output of say "validate" is understandable without the niceties Nx and Nx Cloud offer. It would have been easier to show the before and after if, say, all the stacks were in the same repo.
Even though you say this doesn't really suite the best use case scenario for the blues stack, I'm already really impressed. I think this is very cool. It's not that we need Nx to help with development of the stack. What we're doing here is making it so when people generate a Remix stack the output is a project pre-configured with Nx so people building their remix app have the best possible experience with the CLI during development. So Nx isn't for us developing the template, it's for everyone else using the template. I think this is a nice step up!
Made the changes:
-
Remove all nx-cloud related stuff, remove unused npm dep.
-
On "name": "blues-stack"
Nx most of the time is used for managing repos with multiple projects in them. There is no restriction on what that name is. You change the name to any value. You just need to make sure to change the following in nx.json to the same value:
"cli": {
"defaultProjectName": "blues-stack"
},
- I want to mention that I marked pretty much everything (except dev) cacheable. Caching is cool etc, but you don't have to use it if you only care about process orchestration and the terminal output. If that's the case the cacheableOperations in nx.json can be removed, and the nx section in package.json can be reduced to this:
"targets": {
"test:e2e:run": { "dependsOn": [ "build-all" ] },
"test:e2e:dev": { "dependsOn": [ "build-all" ] }
}
This is looking good. I've updated things a bit to make the name that shows up in the output be what they called their app. One issue I bumped into is I did the following:
- Run
npm run build - Change
server.ts - Run
npm run build
And I noticed that it didn't rebuild the server.ts file. It said it could use the cache. In looking at the config it looks like we only show where the output files go, but we don't say anything about the input files. How does nx know that it can trust the cache without knowing which input files there are? In this case, it's wrong 😬
@vsavkin could you bring this PR up to date and go through the remarks? Thank you 🙏🏼
@vsavkin Could you please rebase on latest main & resolve conflicts + conversations?
Here's a sample remix.config.js I'm using to define routes in each Nx lib project under libs/example-feature/src/lib/routes/ instead of defining all routes in apps/remix-app/app/routes. It also uses watchPaths to only watch referenced libs for file changes rather than everything in the monorepo.
It's not thoroughly tested yet but maybe others using Nx will find it useful. You could consider adding the watchPaths example to this PR.
For the below to work you will need to patch out the code in emptyModulesPlugin that limits it to working on files under /app. https://github.com/remix-run/remix/blob/fd00bc657ef7f2b55c53e5f9dbd77185fa7d5734/packages/remix-dev/compiler/plugins/emptyModulesPlugin.ts#L20
const { createGlobPatternsForDependencies } = require("@nrwl/react/tailwind");
const { defineConventionalRoutes } = require("@remix-run/dev/dist/config/routesConvention");
const path = require("path");
const fs = require("fs");
/**
* @type {import('@remix-run/dev').AppConfig}
*/
module.exports = {
// ignore all files in routes folder
ignoredRouteFiles: ["**/.*"],
// add routes for all referenced lib projects
routes: defineLibraryRoutes,
// watch referenced libs for file changes during dev
watchPaths: () => createGlobPatternsForDependencies(__dirname, "/**"),
};
async function defineLibraryRoutes() {
let routeManifest;
const appFolder = path.join(__dirname, "app");
const libDirectories = createGlobPatternsForDependencies(__dirname, "/lib/");
for (const dir of libDirectories) {
// check if referenced library has routes defined
const routesFolder = path.join(dir, "routes");
if (!fs.existsSync(routesFolder)) continue;
// get all routes from library based on default conventions
let manifest = defineConventionalRoutes(dir, ["**/*.test.{js,jsx,ts,tsx}"]);
// convert route file paths to be relative to app directory
for (const [, route] of Object.entries(manifest)) {
const routeFile = path.join(dir, route.file);
route.file = path.relative(appFolder, routeFile);
}
routeManifest = { ...routeManifest, ...manifest };
}
return routeManifest;
}