deps: react-helmet -> react-helmet-async 2

react-helmet 5 is unmaintained and breaks under React 18 StrictMode.
Same <Helmet> API in the 15 pages; HelmetProvider wraps <App/> in the
client entry. Browser-verified: per-page titles, meta description and
hreflang alternates all inject. REST smoke byte-identical.
This commit is contained in:
vjrj 2026-07-18 06:14:14 +02:00
parent f51acf4d78
commit f1eae84e25
19 changed files with 47 additions and 93 deletions

View file

@ -315,6 +315,7 @@ MONGO_CONTAINER=tcef-mongo7 MONGO_SHELL=mongosh MONGO_PORT=27019 ./smoke/smoke.s
| maximum:server-transform, meteorhacks:zones, less, markdown, test stack | — | — | ✅ removed (dead/unused) |
| fourseven:scss | 4.5.4 | 4.14.1 | ✅ node-12 compatible |
| node-gcm | 1.0.2 | — | ✅ removed (dead API, moved to microservice) |
| react-helmet | 5.2.0 | react-helmet-async 2.0.5 | ✅ done — react-helmet is unmaintained and misbehaves under React 18 StrictMode. Drop-in: same `<Helmet>` children API across the 15 pages; `<HelmetProvider>` wraps `<App/>` in `imports/startup/client/index.js`. Verified in browser: per-page titles, meta description and hreflang alternates injected (`data-rh`). Note: head updates are rAF-deferred (like react-helmet), so hidden/background tabs don't flush — irrelevant for real users. |
| reactstrap | 5.0.0-alpha.3 | — | ✅ removed — zero imports in the codebase (react-bootstrap covers all UI). |
| react-leaflet-sidebarv2 | 0.5.1 | — | ✅ removed — never imported. |
| react-router-hash-link | 1.1.1 | — | ✅ removed — only a commented-out import in Index.js. |

View file

@ -1,6 +1,7 @@
/* global */
import React from 'react';
import { createRoot } from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async';
import { Meteor } from 'meteor/meteor';
import '/imports/startup/client/modernizr';
import App from '../../ui/layouts/App/App';
@ -15,5 +16,9 @@ else {
Modernizr.on('webp', (result) => {
if (Meteor.isDevelopment) console.log(`webp ${result ? '' : 'not '}supported`);
});
Meteor.startup(() => createRoot(document.getElementById('react-root')).render(<App />));
Meteor.startup(() => createRoot(document.getElementById('react-root')).render(
<HelmetProvider>
<App />
</HelmetProvider>
));
}

View file

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import { Router, Switch, Route } from 'react-router-dom';
import { Grid } from 'react-bootstrap';
import { I18nextProvider } from 'react-i18next';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
// https://github.com/gadicc/meteor-blaze-react-component/

View file

@ -9,7 +9,7 @@ import { translate, Trans } from 'react-i18next';
import { Row, Col, Alert, FormGroup } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import { Map, GeoJSON } from 'react-leaflet';
import { rectangleAround } from 'map-common-utils';
import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';

View file

@ -9,7 +9,7 @@ import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { withTracker } from 'meteor/react-meteor-data';
import { Tracker } from 'meteor/tracker';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import { Trans, translate } from 'react-i18next';
import { Map } from 'react-leaflet';
import Control from 'react-leaflet-control';

View file

@ -5,7 +5,7 @@
import 'simple-line-icons/css/simple-line-icons.css';
import React, { Component } from 'react';
import { translate, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import PropTypes from 'prop-types';
// import { Link } from 'react-router-dom';
// https://www.npmjs.com/package/react-resize-detector

View file

@ -6,7 +6,7 @@ import { Row, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import { Meteor } from 'meteor/meteor';
import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import { translate } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n';
import { testId } from '/imports/ui/components/Utils/TestUtils';

View file

@ -2,7 +2,7 @@ import React from 'react';
import { Meteor } from 'meteor/meteor';
import Icon from '../../components/Icon/Icon';
import { translate, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import './Logout.scss';

View file

@ -1,7 +1,7 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
import { translate, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
const NotFound = () => (
<div className="NotFound">

View file

@ -6,7 +6,7 @@ import { ReactiveVar } from 'meteor/reactive-var';
import i18n from '/imports/startup/client/i18n';
import PageHeader from '../../components/PageHeader/PageHeader';
import Content from '../../components/Content/Content';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import './Page.scss';

View file

@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import AccountPageFooter from '../../components/AccountPageFooter/AccountPageFooter';
import validate from '../../../modules/validate';
import { translate } from 'react-i18next';

View file

@ -4,7 +4,7 @@ import { Row, Alert, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import Col from '../../components/Col/Col';
import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import validate from '../../../modules/validate';
import { translate } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n';

View file

@ -10,7 +10,7 @@ import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert';
import { translate, Trans } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import { testId } from '/imports/ui/components/Utils/TestUtils';
import Col from '../../components/Col/Col';
import Icon from '../../components/Icon/Icon';

View file

@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { withTracker } from 'meteor/react-meteor-data';
import Blaze from 'meteor/gadicc:blaze-react-component';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
/*
https://github.com/meteor/docs/blob/version-NEXT/long-form/oplog-observe-driver.md

View file

@ -9,7 +9,7 @@ import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Trans, translate } from 'react-i18next';
import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import SelectionMap, { action } from '/imports/ui/components/SelectionMap/SelectionMap';
import confirm from '/imports/ui/components/Prompt/Confirm';

View file

@ -9,7 +9,7 @@ import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Trans, translate } from 'react-i18next';
import { Map } from 'react-leaflet';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';

View file

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Alert } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import { Helmet } from 'react-helmet';
import { Helmet } from 'react-helmet-async';
import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert';
import { translate } from 'react-i18next';

100
package-lock.json generated
View file

@ -64,7 +64,7 @@
"react-bootstrap": "^0.31.5",
"react-confirm": "^0.1.16",
"react-dom": "^18.3.1",
"react-helmet": "^5.2.0",
"react-helmet-async": "^2.0.5",
"react-i18next": "^7.4.0",
"react-leaflet": "^1.8.0",
"react-leaflet-control": "^1.4.0",
@ -3543,15 +3543,6 @@
"semver": "^5.3.0"
}
},
"node_modules/babel-preset-env/node_modules/invariant": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
"integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
"dev": true,
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/babel-preset-jest": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-21.2.0.tgz",
@ -3817,15 +3808,6 @@
"node": ">=0.10.0"
}
},
"node_modules/babel-traverse/node_modules/invariant": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
"integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
"dev": true,
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/babel-traverse/node_modules/to-fast-properties": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
@ -4951,11 +4933,6 @@
"node": ">=0.10.0"
}
},
"node_modules/deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
},
"node_modules/deep-is": {
"version": "0.1.3",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
@ -6354,11 +6331,6 @@
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"node_modules/exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
},
"node_modules/expand-brackets": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
@ -8406,14 +8378,6 @@
"warning": "^3.0.0"
}
},
"node_modules/history/node_modules/invariant": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
"integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/history/node_modules/warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
@ -8810,8 +8774,10 @@
}
},
"node_modules/invariant": {
"version": "2.2.2",
"integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
@ -19686,26 +19652,24 @@
"react": "^18.3.1"
}
},
"node_modules/react-helmet": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.0.tgz",
"integrity": "sha1-qBgR3yExOm1VxfBYxK66XW89l6c=",
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
"license": "MIT"
},
"node_modules/react-helmet-async": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.5.tgz",
"integrity": "sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==",
"license": "Apache-2.0",
"dependencies": {
"deep-equal": "^1.0.1",
"object-assign": "^4.1.1",
"prop-types": "^15.5.4",
"react-side-effect": "^1.1.0"
"invariant": "^2.2.4",
"react-fast-compare": "^3.2.2",
"shallowequal": "^1.1.0"
},
"peerDependencies": {
"react": ">=15.0.0"
}
},
"node_modules/react-helmet/node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
"react": "^16.6.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-i18next": {
@ -19916,14 +19880,6 @@
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
"integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
},
"node_modules/react-router-dom/node_modules/invariant": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
"integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/react-router-dom/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
@ -19990,15 +19946,6 @@
"react": "^0.13.0 || ^0.14.0 || ^15.0.0 || ^16.0.0-0"
}
},
"node_modules/react-side-effect": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.3.tgz",
"integrity": "sha1-USwlq+DewXKDTEAB7FxR4E1BvFw=",
"dependencies": {
"exenv": "^1.2.1",
"shallowequal": "^1.0.1"
}
},
"node_modules/read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
@ -20763,9 +20710,10 @@
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"node_modules/shallowequal": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz",
"integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw=="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
"license": "MIT"
},
"node_modules/shebang-command": {
"version": "1.2.0",

View file

@ -64,7 +64,7 @@
"react-bootstrap": "^0.31.5",
"react-confirm": "^0.1.16",
"react-dom": "^18.3.1",
"react-helmet": "^5.2.0",
"react-helmet-async": "^2.0.5",
"react-i18next": "^7.4.0",
"react-leaflet": "^1.8.0",
"react-leaflet-control": "^1.4.0",