Skip to content

Instantly share code, notes, and snippets.

@OliverJAsh
Last active February 24, 2023 19:27
Show Gist options
  • Save OliverJAsh/da137c622a662d5e449f3a99c6cd1e79 to your computer and use it in GitHub Desktop.
Save OliverJAsh/da137c622a662d5e449f3a99c6cd1e79 to your computer and use it in GitHub Desktop.
import * as A from 'fp-ts/Array';
import { pipe } from 'fp-ts/function';
import * as IO from 'fp-ts/IO';
// Space: https://unicodeplus.com/U+0020
const space = String.fromCharCode(32);
// Narrow No-Break Space: https://unicodeplus.com/U+202F
const narrowNoBreakSpace = String.fromCharCode(8239);
// Thin Space: https://unicodeplus.com/U+2009
const thinSpace = String.fromCharCode(8201);
/**
* A patched alternative to `Intl.DateTimeFormat`, fixing React hydration for some usages of the
* `format` and `formatRange` methods.
*
* Specifically some platforms will render irregular spaces in place of regular ones. This is caused by
* an ICU mismatch between versions and across platforms. Consider that our server-side React is
* rendered on Linux, so a macOS client at time of writing won't be able to hydrate without this
* patch.
*
* @see https://github.com/unsplash/unsplash-web/pull/9535#issuecomment-1429513353
* @see https://github.com/unicode-org/icu/pull/2103
* @see https://github.com/unicode-org/cldr/pull/2001
*/
class PatchedDateTimeFormat extends Intl.DateTimeFormat {
format(...args: Parameters<Intl.DateTimeFormat['format']>): string {
return super.format(...args).replaceAll(narrowNoBreakSpace, space);
}
formatRange(...args: Parameters<Intl.DateTimeFormat['formatRange']>): string {
return super
.formatRange(...args)
.replaceAll(thinSpace, space)
.replaceAll(narrowNoBreakSpace, space);
}
formatRangeToParts(
...args: Parameters<Intl.DateTimeFormat['formatRangeToParts']>
): Array<Intl.DateTimeRangeFormatPart> {
return pipe(
super.formatRangeToParts(...args),
A.map(
(p): Intl.DateTimeRangeFormatPart => ({
...p,
value: p.value.replaceAll(thinSpace, space).replaceAll(narrowNoBreakSpace, space),
}),
),
);
}
formatToParts(
...args: Parameters<Intl.DateTimeFormat['formatToParts']>
): Array<Intl.DateTimeFormatPart> {
return pipe(
super.formatToParts(...args),
A.map(
(p): Intl.DateTimeFormatPart => ({
...p,
value: p.value.replaceAll(narrowNoBreakSpace, space),
}),
),
);
}
}
const originalDateTimeFormat = Intl.DateTimeFormat;
/**
* Patch `Intl.DateTimeFormat` globally, fixing React hydration. See `PatchedDateTimeFormat`.
*/
export const patch: IO.IO<void> = () => {
Intl.DateTimeFormat = PatchedDateTimeFormat as typeof Intl.DateTimeFormat;
};
export const unpatch: IO.IO<void> = () => {
Intl.DateTimeFormat = originalDateTimeFormat;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment