Contributing to the Pokémon Showdown client means writing modern ES2018 TypeScript that Babel 7 compiles all the way down to ES3 — the JavaScript dialect understood by Internet Explorer 7. This sounds limiting, but in practice Babel handles nearly everything automatically. There are just a handful of language features you must consciously avoid, a small set of polyfills you can rely on, and a few formatting conventions enforced by ESLint.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/smogon/pokemon-showdown-client/llms.txt
Use this file to discover all available pages before exploring further.
Compatibility Targets
The client has two distinct compatibility tiers:Replay Player
Must support IE7 and later. The strictest tier. Avoid any runtime construct that does not compile to valid ES3.
Main Client
Must support IE9, Safari 5, and later. Slightly more relaxed, but still targets ES3 output through Babel.
Things You Cannot Use
Despite writing ES2018 syntax, a few features have no viable ES3 polyfill or produce unacceptable overhead when compiled. Avoid these entirely:Map and Set
Map and Set
Map and Set can technically be polyfilled, but it is better to use plain objects directly.Object.create(null) produces a prototype-free object, which avoids accidental collisions with inherited properties.async / await
async / await
There is no way to compile
async/await to ES3. Promises themselves are fine because they can be shimmed at runtime.Generators and Non-Array Iterables
Generators and Non-Array Iterables
Generator functions and non-
Array iterables either produce enormous compiled output or are outright unsupported. for-of is allowed only on Arrays — Babel transforms it with zero overhead when it can assume Array.Available Polyfills
The following APIs are shimmed at runtime. They are optimized for speed rather than strict spec-compliance — avoid edge cases documented below.| API | Caveat |
|---|---|
Array#includes | Will not find NaN values |
Array.isArray | — |
String#startsWith | — |
String#endsWith | — |
String#includes | — |
String#trim | — |
Object.assign | — |
Object.create | Second argument (property descriptors) is unsupported |
TypeScript Configuration
TypeScript is used throughout the codebase and is checked as part ofnpm run test. The tsconfig.json enables strict mode and a number of additional checks:
noEmit: true means tsc is used only for type-checking. Babel handles the actual compilation to JavaScript. This separation allows Babel to target ES3 while TypeScript checks against modern typings.strict: true— enablesstrictNullChecks,noImplicitAny, and related checks. All types must be explicit or inferrable.noImplicitOverride: true— methods that override a base-class method must be marked with theoverridekeyword.verbatimModuleSyntax: true— type-only imports must useimport typeto allow Babel to strip them safely.jsx: "preserve"— JSX is left as-is bytscand transformed by Babel using the Preact pragma.
Babel Transform Pipeline
The.babelrc defines the full transform pipeline in order. Babel handles everything from TypeScript stripping to ES3 property literal quoting:
TypeScript and JSX transforms
TypeScript and JSX transforms
preact.h(...) calls — not React.createElement. Always import Preact when writing JSX.ESNext transforms (optional chaining, nullish coalescing, etc.)
ESNext transforms (optional chaining, nullish coalescing, etc.)
??, ?., ??=, &&=, and ||= syntax in source while producing ES3-compatible output.ES6 transforms (classes, arrow functions, destructuring, etc.)
ES6 transforms (classes, arrow functions, destructuring, etc.)
assumeArray: true is what makes for-of zero-overhead — it only works correctly when iterating Arrays. Non-Array iterables will silently break at runtime.ES3 property-literal transforms
ES3 property-literal transforms
obj.return to obj["return"]). Even so, be careful: write quoted keys in object literals yourself when using reserved words, since the linter will catch unquoted usage before Babel can fix it.ESLint Configuration
ESLint is configured ineslint.config.mjs using the eslint-ps-standard shared config and @stylistic/eslint-plugin for formatting rules. Three distinct profiles are applied to different file groups:
ES3 (old client)
Files in
play.pokemonshowdown.com/src/oldclient/. Uses configs.es3 with ecmaVersion: 3 and a large list of browser/battle globals.JavaScript for Node
Build tools in
build-tools/ and *.mjs files. Uses configs.js with Node globals.TypeScript
All
.ts and .tsx source files. Uses configs.es3ts with project-service type-aware linting.--max-warnings 0, so every warning is treated as an error in CI.
Notable Rule Overrides
| Rule | Setting | Reason |
|---|---|---|
prefer-const | off (TypeScript files) | Temporarily disabled during the Preact rewrite |
@stylistic/padded-blocks | off | Used intentionally for grouping related code |
@typescript-eslint/no-floating-promises | off | Too many intentional fire-and-forget patterns in the client |
@typescript-eslint/unbound-method | off | Used intentionally for animation callbacks |
JSX with Preact
The client uses Preact — not React. The JSX pragma is set topreact.h in .babelrc. Import Preact explicitly in every file that uses JSX:
File Naming Conventions
Source files follow a consistent naming pattern that reflects their role in the loading phases:| Prefix | Role | Examples |
|---|---|---|
panel- | UI panels (Preact components) | panel-chat.tsx, panel-battle.tsx, panel-mainmenu.tsx |
battle- | Battle simulation and data | battle-dex.ts, battle-animations.ts, battle-log.ts |
client- | Core client infrastructure | client-main.ts, client-connection.ts, client-core.ts |
play.pokemonshowdown.com/src/ for the main client, replay.pokemonshowdown.com/src/ for the replay player, or teams.pokemonshowdown.com/src/ for the teams subsite.
Reserved Word Gotcha
This is worth calling out explicitly. ES3 treats many words as reserved even in property position. Babel’s property-literal transform handles compiled output, but your source code should still use quoted keys to keep the linter happy and intentions clear:return, delete, default, class, extends, import, export, in, instanceof, typeof, void.