Second and final step of #2166. `react-intl` is replaced by `app/common/intl.tsx`, a small i18n binding over Preact context, and with `react-redux` already gone nothing holds the React compatibility alias. React is now absent from the lockfile, the installed tree, the config and the bundles: `react`, `react-dom`, `react-intl`, `@types/react`, `@preact/compat`, `use-sync-external-store` and `intl-messageformat` are all gone, along with the `paths` entries in `tsconfig.json` and three babel-loader excludes. Runtime dependencies go from 15 to 10. `preact/compat` goes too, which matters more than its 3.8 kB. Importing it anywhere installs hooks on preact's shared `options` that remap `onFocus`/`onBlur` to `focusin`/`focusout` for every element and make `@testing-library/preact` rewrite `change` to `input`, the two bugs behind #2166, still live until now. `Button` was wrapped in `forwardRef` with no caller passing one, and `TextareaAutosize` now takes its ref as an ordinary prop. The workaround in `sort-picker.spec.tsx` is gone with them, since `fireEvent.change` reaches a `<select>` again. Gzipped, against master: `remark.mjs` 76.17 kB to 56.47, `last-comments.mjs` 37.97 to 18.26, `deleteme.mjs` 14.51 to 8.42. The limits move with them and keep more relative headroom than master shipped. ### The binding `IntlProvider`, `useIntl`, `createIntl`, `defineMessages`, `FormattedMessage` and `IntlShape`. 32 files change only their import. The export names copy react-intl's deliberately: `formatjs extract` finds messages by recognising `defineMessages`, `FormattedMessage` and `intl.formatMessage` in the AST rather than by import source, so renaming one silently empties the catalogue. `frontend/CLAUDE.md` records that, along with the destructive part: `translation:generate` would then strip the unextracted keys from all 24 catalogues and the check would pass. A message the binding cannot parse falls back to the message in the source: a broken, unhandled or nested tag, a brace that is not a well-formed placeholder, and a placeholder naming a value the caller did not supply. `mk.json` and `th.json` carried broken markup and rendered in English; both are repaired, so a catalogue sweep over every locale can now require well-formed markup with no exceptions listed. `translation:check` gained the validation that would have caught them when they were proposed: a translation's tags have to be well-formed pairs of the names the English string uses, with no attributes, and its placeholders have to be ones the English string provides. Leaving a tag or a placeholder out stays allowed. Run against master's catalogues it reports both. ### enzyme `@types/enzyme` was the last thing pulling `@types/react`, so React could not leave while enzyme stayed. Its three test files move to `@testing-library/preact`, which now has no rival: `@testing-library/preact-hooks` had one import left and its own unmet peer warning. `intersection-observer` was a runtime dependency nothing imported, and the `cheerio` override lost its last dependent with enzyme. Enzyme's `.find(X).prop()` threw unless exactly one node matched, so the converted tests assert node counts explicitly to keep that. ### Verified All 181 message ids formatted across all 24 catalogues through both real react-intl and this binding: 4344 comparisons, no differences. From a wiped `node_modules`: `pnpm install --frozen-lockfile`, `pnpm lint`, `pnpm type-check`, `pnpm test` (392 tests, 42 suites), `pnpm build`, `pnpm size-check`, `pnpm translation-check`.
Remark42 site
Work on your local environment
Requirements:
- Node.js v20 or higher - install from package or with Homebrew
- Yarn 1.22 or higher - once you have Node.js, run
npm i -g yarn
Development
Install dependencies and start the development server:
yarn
yarn dev
Build
yarn build
Work with Docker Compose
Build
Install dependencies and run development server inside Docker:
docker compose build
docker compose up server
Then serve files from ./build with your favorite server
Development
docker compose up --build server
Then head to http://localhost:8080