-
Notifications
You must be signed in to change notification settings - Fork 4
Improve some DX quirks #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This commit completely solves the DX problem with a unified, single dev server that intelligently handles both frontend and server-side changes. ## The Problem Previously, `node --watch` restarted the entire Express server (including Discord bot) on ANY file change in `./app`, causing: - 6-8 second feedback loops for simple UI changes - Discord bot reconnections on every file save - Manual browser refreshes required - Poor developer experience ## The Solution Leverage Vite's existing dev server and module invalidation system: 1. **index.dev.js**: Listen to Vite's file watcher and reload only the server module (`app/server.ts`) when server-side files change, without restarting the entire Node process 2. **Frontend changes**: Vite HMR handles React components/routes instantly (<1s) 3. **Server changes**: Vite invalidates and reloads the server module, applying fresh Discord bot code without full process restart 4. **Discord gateway**: Added `globalThis` flag to prevent duplicate logins during module reloads ## Results - Frontend changes: <1 second (Vite HMR) - Server changes: ~2 seconds (module reload, no Discord reconnect) - Single command: `npm run dev` works for everything - No manual refresh needed for frontend changes - Discord bot stays connected during UI development ## Files Changed - `index.dev.js`: Added intelligent Vite watcher that reloads server module - `package.json`: Removed `--watch` flag (Vite handles it) - `app/discord/gateway.ts`: Added globalThis guard against duplicate init - `app/server.ts`: Suppress Chrome DevTools 404s - `app/helpers/sentry.server.ts`: Conditional Sentry init - `README.md`: Updated dev documentation - `notes/2025-10-27_1.md`: Comprehensive DX evaluation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
I've been moderately unhappy with the linting setup here for a while, mostly it complains at me for code formatting that then instantly gets cleaned up by Prettier, which is just a waste. This looks to catch a lot more useful code errors, like unnecessary nullchecks and sketchy assignment patterns. This also fixes some quirks with the git hooks that should make them more consistent — they had previously been invoking line/etc commands manually; they should instead have referenced npm scripts. Now they'll hopefully be a little more consistent there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements comprehensive code quality improvements focused on TypeScript strict typing, import organization, and developer experience enhancements. The changes modernize the codebase with better type safety, consistent code style, and improved hot module reloading for development.
- Enabled TypeScript strict type checking with
recommendedTypeCheckedandstylisticTypeCheckedESLint rules - Added Prettier import sorting plugin with configured import order (node → external → aliases → relative)
- Implemented intelligent HMR in the dev server that hot-swaps server modules without full restarts
Reviewed Changes
Copilot reviewed 77 out of 79 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Removed dev-client script, updated dev:bot to remove --watch, added import sorting plugin and validation improvements |
| index.dev.js | Completely refactored to implement smart HMR with server module reloading and file watching |
| eslint.config.js | Enhanced with type-checked linting, added inline-type-imports rule, disabled unsafe type rules, added many TypeScript best practices |
| app/server.ts | Added Chrome DevTools 404 suppression, changed || to ?? operators, reorganized imports |
| app/helpers/sentry.server.ts | Added validation to only initialize Sentry with valid DSN |
| app/helpers/dateUtils.ts | Removed unused dateToEntryMap logic in fillDateGaps (now always returns zero template) |
| app/helpers/modResponse.ts | Changed to access potentially undefined array directly without null coalescing |
| app/discord/gateway.ts | Added global flag to prevent duplicate Discord gateway initialization during HMR |
| app/models/session.server.ts | Added explicit type assertions for session data, changed to use ??= operator |
| Multiple route files | Reorganized imports following new import order pattern |
| Multiple helper/model files | Replaced type + import with inline type imports, changed ` |
| notes/ files | Added detailed DX evaluation document and updated code examples with new import patterns |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
app/helpers/modResponse.ts
Outdated
| const newVotes = { | ||
| ...oldVotes, | ||
| [newVote]: uniq((oldVotes[newVote] || []).concat(userId)), | ||
| [newVote]: uniq(oldVotes[newVote].concat(userId)), |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Direct access to oldVotes[newVote] will throw if the key doesn't exist (accessing undefined.concat). This should use oldVotes[newVote] ?? [] or initialize the votes object with empty arrays for all resolutions.
| } | ||
| const { channel, fields, user } = interaction; | ||
| const concern = fields.getField("concern").value; | ||
| const concern = fields.getTextInputValue("concern"); |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed from fields.getField('concern').value to fields.getTextInputValue('concern'). Verify this API exists in the Discord.js version being used. The getTextInputValue method may not be available in older versions.
| const ticketsChannel = config.channel_id | ||
| ? ((await interaction.guild.channels.fetch( | ||
| config.channel_id, | ||
| )) as TextChannel) || channel | ||
| )) as TextChannel) | ||
| : channel; |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the || channel fallback after the type cast. If interaction.guild.channels.fetch() returns null/undefined (e.g., channel deleted), ticketsChannel will be undefined instead of falling back to channel, causing errors when used later.
| export default async (bot: Client) => { | ||
| bot.on("messageCreate", async (msg) => { | ||
| if (msg.author?.id === bot.user?.id || !msg.guild) return; | ||
| if (msg.author.id === bot.user?.id || !msg.guild) return; |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed from optional chaining msg.author?.id to direct access msg.author.id. If msg.author is null/undefined (which can happen with partial messages), this will throw 'Cannot read property id of null/undefined'.
| if (msg.author.id === bot.user?.id || !msg.guild) return; | |
| if (!msg.author || msg.author.id === bot.user?.id || !msg.guild) return; |
- Remove `format:check` in favor of just always writing - Handle a type issue in ticket setup - Don't use implied item type in guildsettingsform - Fix broken dateUtil helper - Disable `no-unnecessary-condition` because it appears to have excessive false positives - Fix broken vote logic - More correctly use `toString` instead of JSON.stringify
I've been moderately unhappy with the linting setup here for a while, mostly it complains at me for code formatting that then instantly gets cleaned up by Prettier, which is just a waste. This looks to catch a lot more useful code errors, like unnecessary nullchecks and sketchy assignment patterns.
This also fixes some quirks with the git hooks that should make them more consistent — they had previously been invoking line/etc commands manually; they should instead have referenced npm scripts. Now they'll hopefully be a little more consistent there.
This should also fix some irritations with local dev restarts, which turned out to be almost set up right. It wasn't correctly reloading the server code, relying on a full restart instead. Now it has HMR for both the React app as well as the Discord app