mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Wrapped public facing strings with translation function
refs: https://github.com/TryGhost/Ghost/issues/16628
This commit is contained in:
parent
5816217019
commit
8135ef74f7
11 changed files with 122 additions and 60 deletions
|
@ -33,10 +33,12 @@
|
|||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"plugin:ghost/browser"
|
||||
"plugin:ghost/browser",
|
||||
"plugin:i18next/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"ghost"
|
||||
"ghost",
|
||||
"i18next"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
|
@ -60,7 +62,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.21.8",
|
||||
"@doist/react-interpolate": "0.4.0",
|
||||
"@doist/react-interpolate": "0.4.1",
|
||||
"@sentry/react": "7.52.1",
|
||||
"@sentry/tracing": "7.53.0",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
|
@ -73,6 +75,7 @@
|
|||
"cross-fetch": "3.1.6",
|
||||
"eslint": "8.37.0",
|
||||
"eslint-config-react-app": "7.0.1",
|
||||
"eslint-plugin-i18next": "^6.0.1",
|
||||
"jsdom": "22.0.0",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
|
|
|
@ -219,10 +219,16 @@ export const GlobalStyles = `
|
|||
padding: 10vmin 28px;
|
||||
}
|
||||
|
||||
.gh-mobile-shortener {
|
||||
.gh-desktop-only {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 481px) {
|
||||
.gh-mobile-only {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default GlobalStyles;
|
||||
export default GlobalStyles;
|
||||
|
|
|
@ -26,6 +26,7 @@ const Styles = () => {
|
|||
};
|
||||
|
||||
const NotificationText = ({type, status, context}) => {
|
||||
const t = context.t;
|
||||
const signinPortalLink = getPortalLink({page: 'signin', siteUrl: context.site.url});
|
||||
const singupPortalLink = getPortalLink({page: 'signup', siteUrl: context.site.url});
|
||||
|
||||
|
@ -33,16 +34,18 @@ const NotificationText = ({type, status, context}) => {
|
|||
const firstname = context.member.firstname || '';
|
||||
return (
|
||||
<p>
|
||||
Welcome back{(firstname ? ', ' + firstname : '')}!<br />You've successfully signed in.
|
||||
{firstname ? t('Welcome back, {{name}}!', firstname) : t('Welcome back!')}<br />{t('You\'ve successfully signed in.')}
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'signin' && status === 'error') {
|
||||
return (
|
||||
<p>
|
||||
Could not sign in. Login link expired. <a href={signinPortalLink} target="_parent">Click here to retry</a>
|
||||
{t('Could not sign in. Login link expired.')} <a href={signinPortalLink} target="_parent">{t('Click here to retry')}</a>
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'signup' && status === 'success') {
|
||||
// TODO: Wrap these strings with translation function
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
return (
|
||||
<p>
|
||||
You've successfully subscribed to <br /><strong>{context.site.title}</strong>
|
||||
|
@ -54,41 +57,42 @@ const NotificationText = ({type, status, context}) => {
|
|||
You've successfully subscribed to <br /><strong>{context.site.title}</strong>
|
||||
</p>
|
||||
);
|
||||
/* eslint-enable i18next/no-literal-string */
|
||||
} else if (type === 'updateEmail' && status === 'success') {
|
||||
return (
|
||||
<p>
|
||||
Success! Your email is updated.
|
||||
{t('Success! Your email is updated.')}
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'updateEmail' && status === 'error') {
|
||||
return (
|
||||
<p>
|
||||
Could not update email! Invalid link.
|
||||
{t('Could not update email! Invalid link.')}
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'signup' && status === 'error') {
|
||||
return (
|
||||
<p>
|
||||
Signup error: Invalid link <br /><a href={singupPortalLink} target="_parent">Click here to retry</a>
|
||||
{t('Signup error: Invalid link')}<br /><a href={singupPortalLink} target="_parent">{t('Click here to retry')}</a>
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'signup-paid' && status === 'error') {
|
||||
return (
|
||||
<p>
|
||||
Signup error: Invalid link <br /><a href={singupPortalLink} target="_parent">Click here to retry</a>
|
||||
{t('Signup error: Invalid link')}<br /><a href={singupPortalLink} target="_parent">{t('Click here to retry')}</a>
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'stripe:checkout' && status === 'success') {
|
||||
if (context.member) {
|
||||
return (
|
||||
<p>
|
||||
Success! Your account is fully activated, you now have access to all content.
|
||||
{t('Success! Your account is fully activated, you now have access to all content.')}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p>
|
||||
Success! Check your email for magic link to sign-in.
|
||||
{t('Success! Check your email for magic link to sign-in.')}
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'stripe:checkout' && status === 'warning') {
|
||||
|
@ -96,19 +100,19 @@ const NotificationText = ({type, status, context}) => {
|
|||
if (context.member) {
|
||||
return (
|
||||
<p>
|
||||
Plan upgrade was cancelled.
|
||||
{t('Plan upgrade was cancelled.')}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p>
|
||||
Plan checkout was cancelled.
|
||||
{t('Plan checkout was cancelled.')}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p>
|
||||
{status === 'success' ? 'Success' : 'Error'}
|
||||
{status === 'success' ? t('Success') : t('Error')}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -207,6 +207,7 @@ export default function NewsletterManagement({
|
|||
className="gh-portal-btn-text gh-email-faq-page-button"
|
||||
onClick={() => onAction('switchPage', {page: 'emailReceivingFAQ'})}
|
||||
>
|
||||
{/* eslint-disable-next-line i18next/no-literal-string */}
|
||||
{t('Get help')} →
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,8 @@ import {ReactComponent as CheckmarkIcon} from '../../images/icons/checkmark-fill
|
|||
import {ReactComponent as WarningIcon} from '../../images/icons/warning-fill.svg';
|
||||
import {getSupportAddress} from '../../utils/helpers';
|
||||
import {clearURLParams} from '../../utils/notifications';
|
||||
import Interpolate from '@doist/react-interpolate';
|
||||
import {SYNTAX_I18NEXT} from '@doist/react-interpolate';
|
||||
|
||||
export const PopupNotificationStyles = `
|
||||
.gh-portal-popupnotification {
|
||||
|
@ -77,24 +79,24 @@ export const PopupNotificationStyles = `
|
|||
}
|
||||
|
||||
@keyframes popupnotification-slidein {
|
||||
0% {
|
||||
transform: translateY(-10px);
|
||||
0% {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
}
|
||||
60% { transform: translateY(2px); }
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popupnotification-slideout {
|
||||
0% {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1.0;
|
||||
}
|
||||
40% { transform: translateY(2px); }
|
||||
100% {
|
||||
100% {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -110,7 +112,7 @@ const CloseButton = ({hide = false, onClose}) => {
|
|||
);
|
||||
};
|
||||
|
||||
const NotificationText = ({message, site}) => {
|
||||
const NotificationText = ({message, site, t}) => {
|
||||
const supportAddress = getSupportAddress({site});
|
||||
const supportAddressMail = `mailto:${supportAddress}`;
|
||||
if (message) {
|
||||
|
@ -119,9 +121,18 @@ const NotificationText = ({message, site}) => {
|
|||
);
|
||||
}
|
||||
return (
|
||||
<p> An unexpected error occured. Please try again or <a href={supportAddressMail} onClick={() => {
|
||||
supportAddressMail && window.open(supportAddressMail);
|
||||
}}>contact support</a> if the error persists.</p>
|
||||
<p>
|
||||
<Interpolate
|
||||
syntax={SYNTAX_I18NEXT}
|
||||
string={t('An unexpected error occured. Please try again or <a>contact support</a> if the error persists.')}
|
||||
mapping={{
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
a: <a href={supportAddressMail} onClick={() => {
|
||||
supportAddressMail && window.open(supportAddressMail);
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -187,7 +198,7 @@ export default class PopupNotification extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {popupNotification, site} = this.context;
|
||||
const {popupNotification, site, t} = this.context;
|
||||
const {className} = this.state;
|
||||
const {type, status, closeable, message} = popupNotification;
|
||||
const statusClass = status ? ` ${status}` : '';
|
||||
|
@ -196,9 +207,9 @@ export default class PopupNotification extends React.Component {
|
|||
return (
|
||||
<div className={`gh-portal-popupnotification${statusClass}${slideClass}`} onAnimationEnd={e => this.onAnimationEnd(e)}>
|
||||
{(status === 'error' ? <WarningIcon className='gh-portal-popupnotification-icon error' alt=''/> : <CheckmarkIcon className='gh-portal-popupnotification-icon success' alt=''/>)}
|
||||
<NotificationText type={type} status={status} message={message} site={site} />
|
||||
<NotificationText type={type} status={status} message={message} site={site} t={t} />
|
||||
<CloseButton hide={!closeable} onClose={e => this.closeNotification(e)}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ export default class PoweredBy extends React.Component {
|
|||
render() {
|
||||
// Note: please do not wrap "Powered by Ghost" in the translation function, as we don't
|
||||
// want it to be translated
|
||||
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
return (
|
||||
<a href='https://ghost.org' target='_blank' rel='noopener noreferrer' onClick={() => {
|
||||
window.open('https://ghost.org', '_blank');
|
||||
|
@ -17,5 +17,6 @@ export default class PoweredBy extends React.Component {
|
|||
Powered by Ghost
|
||||
</a>
|
||||
);
|
||||
/* eslint-enable i18next/no-literal-string */
|
||||
}
|
||||
}
|
||||
|
|
|
@ -814,7 +814,7 @@ function ProductCards({products, selectedInterval, handleChooseSignup, errors})
|
|||
}
|
||||
|
||||
function YearlyDiscount({discount, trialDays}) {
|
||||
const {site} = useContext(AppContext);
|
||||
const {site, t} = useContext(AppContext);
|
||||
const {portal_plans: portalPlans} = site;
|
||||
|
||||
if (discount === 0 || !portalPlans.includes('monthly')) {
|
||||
|
@ -824,13 +824,13 @@ function YearlyDiscount({discount, trialDays}) {
|
|||
if (hasFreeTrialTier({site})) {
|
||||
return (
|
||||
<>
|
||||
<span className="gh-portal-discount-label-trial">{discount}% discount</span>
|
||||
<span className="gh-portal-discount-label-trial">{t('{{discountPercent}} discount', {discountPercent: discount + '%'})}</span>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<span className="gh-portal-discount-label">{discount}% discount</span>
|
||||
<span className="gh-portal-discount-label">{t('{{discountPercent}} discount', {discountPercent: discount + '%'})}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1034,7 +1034,7 @@ function ProductDescription({product, selectedPrice, activePrice}) {
|
|||
}
|
||||
|
||||
function ChangeProductCard({product, onPlanSelect}) {
|
||||
const {member, site} = useContext(AppContext);
|
||||
const {member, site, t} = useContext(AppContext);
|
||||
const {selectedProduct, setSelectedProduct, selectedInterval} = useContext(ProductsContext);
|
||||
const cardClass = selectedProduct === product.id ? 'gh-portal-product-card checked' : 'gh-portal-product-card';
|
||||
const monthlyPrice = product.monthlyPrice;
|
||||
|
@ -1061,7 +1061,7 @@ function ChangeProductCard({product, onPlanSelect}) {
|
|||
</div>
|
||||
{(currentPlan ?
|
||||
<div className='gh-portal-btn-product'>
|
||||
<span className='gh-portal-current-plan'><span>Current plan</span></span>
|
||||
<span className='gh-portal-current-plan'><span>{t('Current plan')}</span></span>
|
||||
</div>
|
||||
:
|
||||
<div className='gh-portal-btn-product'>
|
||||
|
@ -1071,7 +1071,7 @@ function ChangeProductCard({product, onPlanSelect}) {
|
|||
onClick={() => {
|
||||
onPlanSelect(null, selectedPrice?.id);
|
||||
}}
|
||||
>Choose</button>
|
||||
>{t('Choose')}</button>
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,6 +17,7 @@ export default class SiteTitleBackButton extends React.Component {
|
|||
this.context.onAction('closePopup');
|
||||
}
|
||||
}}>
|
||||
{/* eslint-disable-next-line i18next/no-literal-string */}
|
||||
<span>← </span> {t('Back')}
|
||||
</button>
|
||||
</>
|
||||
|
|
|
@ -17,7 +17,8 @@ function EmailPreferencesAction() {
|
|||
? (
|
||||
<p className="gh-portal-email-notice">
|
||||
<EmailDeliveryFailedIcon className="gh-portal-email-notice-icon" />
|
||||
<span>You're <span className="gh-mobile-shortener">currently </span>not receiving emails</span>
|
||||
<span className="gh-mobile-only">{t('You\'re not receiving emails')}</span>
|
||||
<span className="gh-desktop-only">{t('You\'re currently not receiving emails')}</span>
|
||||
</p>
|
||||
)
|
||||
: <p>{t('Update your preferences')}</p>
|
||||
|
|
|
@ -431,7 +431,7 @@ export default class OfferPage extends React.Component {
|
|||
}
|
||||
|
||||
renderOfferTag() {
|
||||
const {pageData: offer} = this.context;
|
||||
const {pageData: offer, t} = this.context;
|
||||
|
||||
if (offer.amount <= 0) {
|
||||
return (
|
||||
|
@ -441,18 +441,20 @@ export default class OfferPage extends React.Component {
|
|||
|
||||
if (offer.type === 'fixed') {
|
||||
return (
|
||||
<h5 className="gh-portal-discount-label">{getCurrencySymbol(offer.currency)}{offer.amount / 100} off</h5>
|
||||
<h5 className="gh-portal-discount-label">{t('{{amount}} off', {
|
||||
amount: `${getCurrencySymbol(offer.currency)}${offer.amount / 100}`
|
||||
})}</h5>
|
||||
);
|
||||
}
|
||||
|
||||
if (offer.type === 'trial') {
|
||||
return (
|
||||
<h5 className="gh-portal-discount-label">{offer.amount} days free</h5>
|
||||
<h5 className="gh-portal-discount-label">{t('{{amount}} days free', {amount: offer.amount})}</h5>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<h5 className="gh-portal-discount-label">{offer.amount}% off</h5>
|
||||
<h5 className="gh-portal-discount-label">{t('{{amount}} off', {amount: offer.amount + '%'})}</h5>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -515,32 +517,51 @@ export default class OfferPage extends React.Component {
|
|||
return '';
|
||||
}
|
||||
|
||||
renderOfferMessage({offer, product, price}) {
|
||||
const discountDuration = offer.duration;
|
||||
let durationLabel = '';
|
||||
renderOfferMessage({offer, product, price, t}) {
|
||||
const offerMessages = {
|
||||
forever: t(`{{amount}} off forever.`, {
|
||||
amount: this.getOffAmount({offer})
|
||||
}),
|
||||
firstPeriod: t(`{{amount}} off for first {{period}}.`, {
|
||||
amount: this.getOffAmount({offer}),
|
||||
period: offer.cadence
|
||||
}),
|
||||
firstNMonths: t(`{{amount}} off for first {{number}} months.`, {
|
||||
amount: this.getOffAmount({offer}),
|
||||
number: offer.duration_in_months || ''
|
||||
})
|
||||
};
|
||||
|
||||
const originalPrice = this.getOriginalPrice({offer, product});
|
||||
let renewsLabel = '';
|
||||
const renewsLabel = t(`Renews at {{price}}.`, {price: originalPrice});
|
||||
|
||||
let offerLabel = '';
|
||||
let useRenewsLabel = false;
|
||||
const discountDuration = offer.duration;
|
||||
if (discountDuration === 'once') {
|
||||
durationLabel = `for first ${offer.cadence}`;
|
||||
renewsLabel = `Renews at ${originalPrice}.`;
|
||||
offerLabel = offerMessages.firstPeriod;
|
||||
useRenewsLabel = true;
|
||||
} else if (discountDuration === 'forever') {
|
||||
durationLabel = `forever`;
|
||||
offerLabel = offerMessages.forever;
|
||||
} else if (discountDuration === 'repeating') {
|
||||
const durationInMonths = offer.duration_in_months || '';
|
||||
if (durationInMonths === 1) {
|
||||
durationLabel = `for first month`;
|
||||
offerLabel = offerMessages.firstPeriod;
|
||||
} else {
|
||||
durationLabel = `for first ${durationInMonths} months`;
|
||||
offerLabel = offerMessages.firstNMonths;
|
||||
}
|
||||
renewsLabel = `Renews at ${originalPrice}.`;
|
||||
useRenewsLabel = true;
|
||||
}
|
||||
if (discountDuration === 'trial') {
|
||||
return (
|
||||
<p className="footnote">Try free for {offer.amount} days, then {originalPrice}. <span class="gh-portal-cancel">Cancel anytime.</span></p>
|
||||
<p className="footnote">{t('Try free for {{amount}} days, then {{originalPrice}}.', {
|
||||
amount: offer.amount,
|
||||
originalPrice: originalPrice
|
||||
})} <span class="gh-portal-cancel">{t('Cancel anytime.')}</span></p>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className="footnote">{this.getOffAmount({offer})} off {durationLabel}. {renewsLabel}</p>
|
||||
<p className="footnote">{offerLabel} {useRenewsLabel ? renewsLabel : ''}</p>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -611,7 +632,7 @@ export default class OfferPage extends React.Component {
|
|||
{(benefits.length ? this.renderBenefits({product}) : '')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className='gh-portal-btn-container sticky m32'>
|
||||
<div className='gh-portal-signup-terms-wrapper'>
|
||||
{this.renderSignupTerms()}
|
||||
|
@ -625,7 +646,7 @@ export default class OfferPage extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {pageData: offer, site} = this.context;
|
||||
const {pageData: offer, site, t} = this.context;
|
||||
if (!offer) {
|
||||
return null;
|
||||
}
|
||||
|
@ -647,7 +668,7 @@ export default class OfferPage extends React.Component {
|
|||
|
||||
<div className="gh-portal-offer-bar">
|
||||
<div className="gh-portal-offer-title">
|
||||
{(offer.display_title ? <h4>{offer.display_title}</h4> : <h4 className='placeholder'>Black Friday</h4>)}
|
||||
{(offer.display_title ? <h4>{offer.display_title}</h4> : <h4 className='placeholder'>{t('Black Friday')}</h4>)}
|
||||
{this.renderOfferTag()}
|
||||
</div>
|
||||
{(offer.display_description ? <p>{offer.display_description}</p> : '')}
|
||||
|
|
21
yarn.lock
21
yarn.lock
|
@ -2034,10 +2034,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@ebay/nice-modal-react/-/nice-modal-react-1.2.10.tgz#6b2406bfce4a5daffc43f5b85f5f238311cdfe93"
|
||||
integrity sha512-qNp8vQo5kPRwB9bHlkh8lcwH/0KFWpp58X/b9KaLB/gNlJ3W24nCT2l/qBBSnWgV7NEIq25uLowaPS2mbfpZiw==
|
||||
|
||||
"@doist/react-interpolate@0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@doist/react-interpolate/-/react-interpolate-0.4.0.tgz#b22a408ec78374213e0148473f082dd4c9a72285"
|
||||
integrity sha512-hGSaTKnY5U6F3/MvuoZfGvtN4nbpL2NhL7c8atQqm0Yn4E2I4ChQQiMxm9/3NEOzd6VfyFxuK9Eo85ogTIO/PA==
|
||||
"@doist/react-interpolate@0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@doist/react-interpolate/-/react-interpolate-0.4.1.tgz#696d14e90ffc849e94a4e3e94578c5eade1590b8"
|
||||
integrity sha512-JxBJMpDlXByMrA7T6Z+tRdCqXlbnM1ikfasLaqMrWvV4TvvrPuD7dngU/X01iP9tpAycfxasNvbNKFs/QehViQ==
|
||||
|
||||
"@elastic/elasticsearch@8.6.0":
|
||||
version "8.6.0"
|
||||
|
@ -16801,6 +16801,14 @@ eslint-plugin-ghost@3.0.0:
|
|||
eslint-plugin-sort-imports-es6-autofix "0.6.0"
|
||||
eslint-plugin-unicorn "42.0.0"
|
||||
|
||||
eslint-plugin-i18next@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-i18next/-/eslint-plugin-i18next-6.0.1.tgz#b0a289179598b247a73881abccb4b73ea8a5e835"
|
||||
integrity sha512-aJ0UlZLVqBK7mdXsl3yFQNz72OPUjSmBfcrMjCNxfMp8YVBAlKE83ZfzzTLCMQvFlXZs0TWYTtoteO/ZiZdDdw==
|
||||
dependencies:
|
||||
lodash "^4.17.21"
|
||||
requireindex "~1.1.0"
|
||||
|
||||
eslint-plugin-import@^2.25.3:
|
||||
version "2.27.5"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65"
|
||||
|
@ -29227,6 +29235,11 @@ requireindex@^1.2.0:
|
|||
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef"
|
||||
integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==
|
||||
|
||||
requireindex@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162"
|
||||
integrity sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
|
|
Loading…
Add table
Reference in a new issue