Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: drop
Intl
polyfill, polyfill ListFormat & Unit Number Format
BREAKING CHANGE: Framework now requires a minimum of Safari 10.3, as the base "Intl" object is no longer polyfilled
- Loading branch information
Showing
7 changed files
with
148 additions
and
97 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import global from 'global'; | ||
import globalThis from '../../shared/globalThis'; | ||
|
||
const debug = {}; | ||
global.rjsDebug = debug; | ||
globalThis.rjsDebug = debug; | ||
|
||
export default debug; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
|
||
// this file requires and installs all locales for intl-pluralrules | ||
// as those locales are tiny, individual lazy-loading produces more code than just loading them individually | ||
installAll(require.context('@formatjs/intl-pluralrules/dist/locale-data', true, /\.json$/)); | ||
|
||
function installAll(r) { | ||
for (const key of r.keys()) { | ||
Intl.RelativeTimeFormat.__addLocaleData(r(key)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,130 +1,155 @@ | ||
// @flow | ||
|
||
import global from 'global'; | ||
import globalThis from '../../../shared/globalThis'; | ||
import { getFileName, getLocaleBestFit, runBundleLoader } from './_locale-utils'; | ||
|
||
// Node should always have these available, no need for polyfilling | ||
// TODO: listFormat | ||
|
||
function getNumberDateTimeLocaleLoaders() { | ||
function getUnitLocaleLoaders() { | ||
// $FlowIgnore | ||
return require.context('bundle-loader?lazy&name=p-intl-[name]!intl/locale-data/jsonp', true, /\.js$/); | ||
return require.context('bundle-loader?lazy&name=p-intlunit-[name]!@formatjs/intl-unified-numberformat/dist/locale-data', true, /\.json$/); | ||
} | ||
|
||
function getRelativeTimeLocaleLoaders() { | ||
// $FlowIgnore | ||
return require.context('bundle-loader?lazy&name=p-intlrelative-[name]!@formatjs/intl-relativetimeformat/dist/locale-data', true, /\.js$/); | ||
return require.context('bundle-loader?lazy&name=p-intlrelative-[name]!@formatjs/intl-relativetimeformat/dist/locale-data', true, /\.json$/); | ||
} | ||
|
||
function areNumberAndDateTimeSupported(locale: string): boolean { | ||
const Intl = global.Intl; | ||
function getListFormatLoaders() { | ||
// $FlowIgnore | ||
return require.context('bundle-loader?lazy&name=p-intllist-[name]!@formatjs/intl-listformat/dist/locale-data', true, /\.js$/); | ||
} | ||
|
||
if (!Intl) { | ||
return false; | ||
} | ||
function isUnifiedNumberFormatSupported(locale: string): boolean { | ||
return globalThis.Intl.NumberFormat.supportedLocalesOf([locale]).length !== 0; | ||
} | ||
|
||
if (!Intl.DateTimeFormat | ||
|| !Intl.NumberFormat | ||
|| !Intl.DateTimeFormat.supportedLocalesOf | ||
|| !Intl.NumberFormat.supportedLocalesOf) { | ||
return false; | ||
} | ||
const hasNativeUnifiedNumberFormat: boolean = (() => { | ||
try { | ||
new globalThis.Intl.NumberFormat('en-US', { style: 'unit', unit: 'meter' }).format(0); | ||
|
||
if (Intl.DateTimeFormat.supportedLocalesOf([locale]).length === 0) { | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
})(); | ||
|
||
if (Intl.NumberFormat.supportedLocalesOf([locale]).length === 0) { | ||
return false; | ||
async function installUnifiedNumberFormat(localeName: string) { | ||
// install polyfill if unified number format is not natively supported | ||
if (hasNativeUnifiedNumberFormat) { | ||
return; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function downloadNumberAndDateTime() { | ||
return import(/* webpackChunkName: "p-intl" */ 'intl'); | ||
} | ||
|
||
function installNumberAndDateTime(localeName: string) { | ||
// load base DateTimeFormat / NumberFormat | ||
const intlPromise = global.Intl ? Promise.resolve() : downloadNumberAndDateTime(); | ||
await import(/* webpackChunkName: "p-intlunit" */'@formatjs/intl-unified-numberformat/polyfill'); | ||
|
||
// load locale data | ||
return intlPromise.then(() => { | ||
if (areNumberAndDateTimeSupported(localeName)) { | ||
return null; | ||
} | ||
if (isUnifiedNumberFormatSupported(localeName)) { | ||
return; | ||
} | ||
|
||
// load locale best fit | ||
// Note: this should never be called on non-webpack generated builds | ||
const intlLocaleLoaders = getNumberDateTimeLocaleLoaders(); | ||
const availableIntlLocales: string[] = intlLocaleLoaders.keys().map(getFileName); | ||
// load locale best fit | ||
// Note: this should never be called on non-webpack generated builds | ||
const intlLocaleLoaders = getUnitLocaleLoaders(); | ||
const availableIntlLocales: string[] = intlLocaleLoaders.keys().map(getFileName); | ||
|
||
let actualLocale = getLocaleBestFit(localeName, availableIntlLocales); | ||
if (actualLocale == null) { | ||
console.error(`Could not fetch Intl locale '${localeName}'. Fallback to 'en'.`); | ||
let actualLocale = getLocaleBestFit(localeName, availableIntlLocales); | ||
if (actualLocale == null) { | ||
console.error(`Could not fetch Unified Number Format locale '${localeName}'. Fallback to 'en'.`); | ||
|
||
// TODO(DEFAULT_LOCALE): use default locale instead of 'en' | ||
actualLocale = 'en'; | ||
} | ||
// TODO(DEFAULT_LOCALE): use default locale instead of 'en' | ||
actualLocale = 'en'; | ||
} | ||
|
||
const loader = intlLocaleLoaders(`./${actualLocale}.js`); | ||
const loader = intlLocaleLoaders(`./${actualLocale}.json`); | ||
const localeModule = await runBundleLoader(loader); | ||
|
||
return runBundleLoader(loader); | ||
}); | ||
Intl.NumberFormat.__addLocaleData(localeModule); | ||
} | ||
|
||
function installPluralRules(localeName: string) { | ||
const Intl = global.Intl; | ||
async function installPluralRules(localeName: string) { | ||
const Intl = globalThis.Intl; | ||
|
||
if (Intl | ||
&& Intl.PluralRules | ||
if (Intl.PluralRules | ||
&& Intl.PluralRules.supportedLocalesOf | ||
&& Intl.PluralRules.supportedLocalesOf(localeName).length !== 0) { | ||
return Promise.resolve(); | ||
return; | ||
} | ||
|
||
// the overhead generated by the lazy-loader of each individual locale | ||
// is equivalent to the overhead of loading all locales | ||
// so might as well only impact people without the polyfill. | ||
return import(/* webpackChunkName: "p-intlplural" */ '@formatjs/intl-pluralrules/polyfill-locales'); | ||
await import(/* webpackChunkName: "p-intlplural" */ '@formatjs/intl-pluralrules/polyfill'); | ||
await import(/* webpackChunkName: "p-intlplural" */ './_intl-plural-locales'); | ||
} | ||
|
||
function installRelativeTime(localeName: string) { | ||
const Intl = global.Intl; | ||
async function installRelativeTime(localeName: string) { | ||
const Intl = globalThis.Intl; | ||
|
||
if (Intl | ||
&& Intl.RelativeTimeFormat | ||
&& Intl.RelativeTimeFormat.supportedLocalesOf | ||
&& Intl.RelativeTimeFormat.supportedLocalesOf(localeName).length !== 0) { | ||
return Promise.resolve(); | ||
return; | ||
} | ||
|
||
await import(/* webpackChunkName: "p-intlrelative" */ '@formatjs/intl-relativetimeformat/polyfill'); | ||
|
||
// load locale best fit | ||
// Note: this should never be called on non-webpack generated builds | ||
const localeLoaders = getRelativeTimeLocaleLoaders(); | ||
const availableLocales: string[] = localeLoaders.keys().map(getFileName); | ||
|
||
let actualLocale = getLocaleBestFit(localeName, availableLocales); | ||
if (actualLocale == null) { | ||
console.error(`Could not fetch Intl.RelativeTimeFormat locale '${localeName}'. Fallback to 'en'.`); | ||
|
||
// TODO(DEFAULT_LOCALE): use default locale instead of 'en' | ||
actualLocale = 'en'; | ||
} | ||
|
||
const loader = localeLoaders(`./${actualLocale}.json`); | ||
|
||
const localeModule = await runBundleLoader(loader); | ||
Intl.RelativeTimeFormat.__addLocaleData(localeModule); | ||
} | ||
|
||
async function installListFormat(localeName: string) { | ||
const Intl = globalThis.Intl; | ||
|
||
if (Intl | ||
&& Intl.ListFormat | ||
&& Intl.ListFormat.supportedLocalesOf | ||
&& Intl.ListFormat.supportedLocalesOf(localeName).length !== 0) { | ||
return; | ||
} | ||
|
||
return import(/* webpackChunkName: "p-intlrelative" */ '@formatjs/intl-relativetimeformat/polyfill').then(() => { | ||
await import(/* webpackChunkName: "p-intllist" */ '@formatjs/intl-listformat/polyfill'); | ||
|
||
// load locale best fit | ||
// Note: this should never be called on non-webpack generated builds | ||
const localeLoaders = getRelativeTimeLocaleLoaders(); | ||
const availableLocales: string[] = localeLoaders.keys().map(getFileName); | ||
// load locale best fit | ||
// Note: this should never be called on non-webpack generated builds | ||
const localeLoaders = getListFormatLoaders(); | ||
const availableLocales: string[] = localeLoaders.keys().map(getFileName); | ||
|
||
let actualLocale = getLocaleBestFit(localeName, availableLocales); | ||
if (actualLocale == null) { | ||
console.error(`Could not fetch Intl.RelativeTimeFormat locale '${localeName}'. Fallback to 'en'.`); | ||
let actualLocale = getLocaleBestFit(localeName, availableLocales); | ||
if (actualLocale == null) { | ||
console.error(`Could not fetch Intl.ListFormat locale '${localeName}'. Fallback to 'en'.`); | ||
|
||
// TODO(DEFAULT_LOCALE): use default locale instead of 'en' | ||
actualLocale = 'en'; | ||
} | ||
// TODO(DEFAULT_LOCALE): use default locale instead of 'en' | ||
actualLocale = 'en'; | ||
} | ||
|
||
const loader = localeLoaders(`./${actualLocale}.js`); | ||
const loader = localeLoaders(`./${actualLocale}.js`); | ||
|
||
return runBundleLoader(loader); | ||
}); | ||
// js version as json version is not supported | ||
await runBundleLoader(loader); | ||
} | ||
|
||
export function installIntlLocale(localeName: string): Promise<void> { | ||
|
||
return Promise.all([ | ||
installNumberAndDateTime(localeName), | ||
installUnifiedNumberFormat(localeName), | ||
installPluralRules(localeName), | ||
installRelativeTime(localeName), | ||
installListFormat(localeName), | ||
]).then(() => void 0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* eslint-disable */ | ||
|
||
// https://mathiasbynens.be/notes/globalthis | ||
const globalObject = (function () { | ||
if (typeof globalThis === 'object') { | ||
return globalThis; | ||
} | ||
|
||
Object.defineProperty(Object.prototype, '__magic__', { | ||
get() { | ||
return this; | ||
}, | ||
configurable: true, // This makes it possible to `delete` the getter later. | ||
}); | ||
const globalObject = __magic__; // get __magic__ from global object's prototype, which is Object.prototype | ||
delete Object.prototype.__magic__; | ||
|
||
return globalObject; | ||
}()); | ||
|
||
export default globalObject; |