Skip to content

Modernize React TypeScript template#9

Open
scobo wants to merge 20 commits into
phaserjs:mainfrom
scobo:modernize-template
Open

Modernize React TypeScript template#9
scobo wants to merge 20 commits into
phaserjs:mainfrom
scobo:modernize-template

Conversation

@scobo
Copy link
Copy Markdown

@scobo scobo commented Mar 9, 2026

  • Updates dependency versions.
  • Converts all files to TypeScript.
  • Modernizes use of React 19 hooks.
  • Implements the React Compiler for automatic memoization.
  • Improves ESLint configuration and addresses all lint issues.
  • Adds Stylelint for CSS formatting and addresses all issues.
  • Creates # path alias for src to avoid relative path imports.
  • Replaces .editorconfig with Prettier for code formatting.
  • Uses CSS modules for typed class names with locally-scoped namespaces.
  • Adds Husky and lint-staged for automatic checking and fixing of formatting and lint issues.
  • Adds __DEV__ global for gating development-only code.
  • Updates log.ts to report the template type instead of the package name, as the README.md indicates it should.
  • Optimizes images.

Note

Medium Risk
Mostly tooling/configuration and documentation changes, but it alters the developer workflow (new Husky/lint-staged hooks, ESLint strict type-checked config, Stylelint/Prettier) and build scripts/logging, which can break local CI/builds if misconfigured.

Overview
Modernizes the template’s tooling and contributor workflow by replacing legacy configs (.eslintrc.cjs, .editorconfig) with ESLint flat config (eslint.config.ts), Prettier, and Stylelint, plus new Husky pre-commit/pre-push hooks and lint-staged-driven auto-fixing.

Updates project metadata/docs (README.md, index.html), tweaks repo hygiene (.gitignore, .npmrc, .nvmrc), and migrates the anonymous build logger from log.js to TypeScript (scripts/log.ts) with an updated reporting path.

Written by Cursor Bugbot for commit 479b9e7. This will update automatically on new commits. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Free Tier Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread eslint.config.ts Outdated
@cursor
Copy link
Copy Markdown

cursor Bot commented Mar 13, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Comment thread eslint.config.ts
import: importPlugin,
"simple-import-sort": simpleImportSort,
},
extends: ["js/recommended"],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 35: extends: ["js/recommended"] inside a flat config object is redundant and likely invalid here, as js.configs.recommended is already included at the top level (Line 24). Please remove this to avoid configuration conflicts or bloat.

Comment thread eslint.config.ts
languageOptions: {
globals: {
...globals.node,
...globals.es2024,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 110: You're using globals.es2024 for node scripts, but globals.es2020 for the main application (Line 39). Unless there is a specific reason to restrict the main app to ES2020, consider standardizing on a newer target or at least matching the node script configuration to reduce drift.

Comment thread scripts/log.ts
const req = https.request(options);

req.on("error", (error) => {
throw new Error(error.message);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing an Error inside an asynchronous req.on('error', ...) handler will not be caught by the caller and will likely result in an unhandled promise rejection or process crash depending on the environment. Please log the error using the project's logging service instead of throwing.

Comment thread scripts/log.ts
`📢 Logging template type, build type, and Phaser version...\n\n`,
);

const req = https.request(options);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script makes a network request but does not wait for it to complete or handle the lifecycle properly. If this script is run as part of a build process, the process might exit before the request finishes. Consider wrapping this in an async function and awaiting the request.

Comment thread scripts/log.ts
const main = () => {
const args = process.argv.slice(2);
const event = args[0] || "unknown";
const phaserVersion = packageData.dependencies.phaser;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

packageData.dependencies is accessed directly. This is unsafe if the package.json structure changes or dependencies is missing. Please add a check or use optional chaining: packageData.dependencies?.phaser.

Comment thread scripts/log.ts
`📢 Logging template type, build type, and Phaser version...\n\n`,
);

const req = https.request(options);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using https.request without handling the response (req.on('response', ...) or req.end()) is dangerous. While req.end() is present, the absence of a response handler means you're not confirming the success of the telemetry. At a minimum, consume the response stream to ensure the connection completes correctly.

Comment thread src/app/App.tsx
const phaserRef = useRef<IRefPhaserGame | null>(null);
const [spritePosition, setSpritePosition] = useState({ x: 0, y: 0 });

const changeScene = () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Event handlers like changeScene, moveSprite, and addSprite are recreated on every render. Since they are passed to DOM buttons, wrap these in useCallback to avoid unnecessary re-renders.

Comment thread src/app/App.tsx
};

// Event emitted from the PhaserGame component
const currentScene = (scene: Phaser.Scene) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentScene is passed as a prop to PhaserGame. Wrap this in useCallback to prevent PhaserGame from re-rendering unnecessarily every time App re-renders.

Comment thread src/app/App.tsx
import { MainMenu } from "#/game/scenes/MainMenu";
import { IRefPhaserGame, PhaserGame } from "#/phaser-game/PhaserGame";

const App = () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component renders purely based on state. Please wrap it in React.memo to improve performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants