feat(v2): polyfill automatically depending on browserslist (#1857)

* feat(v2): polyfill corejs and ie11 compat

* use corejs/stable

* useBuiltIns => usage instead of entry

* chngelo

* nits

* breaking: withBaseUrl -> useBaseUrl

* nits

* loose mode for faster perf and smaller bundle
This commit is contained in:
Endi 2019-10-21 11:09:16 +07:00 committed by GitHub
parent ee936f62b5
commit abdc647b07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 181 additions and 163 deletions

View file

@ -10,7 +10,7 @@ import classnames from 'classnames';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
import styles from './styles.module.css';
const features = [
@ -46,6 +46,21 @@ const features = [
},
];
function Feature({imageUrl, title, description}) {
const imgUrl = useBaseUrl(imageUrl);
return (
<div className={classnames('col col--4', styles.feature)}>
{imgUrl && (
<div className="text--center">
<img className={styles.featureImage} src={imgUrl} alt={title} />
</div>
)}
<h3>{title}</h3>
<p>{description}</p>
</div>
);
}
function Home() {
const context = useDocusaurusContext();
const {siteConfig = {}} = context;
@ -63,7 +78,7 @@ function Home() {
'button button--outline button--secondary button--lg',
styles.getStarted,
)}
to={withBaseUrl('docs/doc1')}>
to={useBaseUrl('docs/doc1')}>
Get Started
</Link>
</div>
@ -74,22 +89,8 @@ function Home() {
<section className={styles.features}>
<div className="container">
<div className="row">
{features.map(({imageUrl, title, description}, idx) => (
<div
key={idx}
className={classnames('col col--4', styles.feature)}>
{imageUrl && (
<div className="text--center">
<img
className={styles.featureImage}
src={withBaseUrl(imageUrl)}
alt={title}
/>
</div>
)}
<h3>{title}</h3>
<p>{description}</p>
</div>
{features.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>

View file

@ -9,7 +9,7 @@ import React from 'react';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
import DocPaginator from '@theme/DocPaginator';
import styles from './styles.module.css';
@ -45,6 +45,8 @@ function DocItem(props) {
keywords,
} = metadata;
const metaImageUrl = siteUrl + useBaseUrl(metaImage);
return (
<div>
<Head>
@ -56,18 +58,8 @@ function DocItem(props) {
{keywords && keywords.length && (
<meta name="keywords" content={keywords.join(',')} />
)}
{metaImage && (
<meta
property="og:image"
content={siteUrl + withBaseUrl(metaImage)}
/>
)}
{metaImage && (
<meta
property="twitter:image"
content={siteUrl + withBaseUrl(metaImage)}
/>
)}
{metaImage && <meta property="og:image" content={metaImageUrl} />}
{metaImage && <meta property="twitter:image" content={metaImageUrl} />}
{metaImage && (
<meta name="twitter:image:alt" content={`Image for ${title}`} />
)}

View file

@ -10,7 +10,27 @@ import classnames from 'classnames';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
function FooterLink({item}) {
const toUrl = useBaseUrl(item.to);
return (
<Link
className="footer__link-item"
{...item}
{...(item.href
? {
target: '_blank',
rel: 'noopener noreferrer',
href: item.href,
}
: {
to: toUrl,
})}>
{item.label}
</Link>
);
}
function Footer() {
const context = useDocusaurusContext();
@ -18,12 +38,13 @@ function Footer() {
const {themeConfig = {}} = siteConfig;
const {footer} = themeConfig;
const {copyright, links = [], logo = {}} = footer || {};
const logoUrl = useBaseUrl(logo.src);
if (!footer) {
return null;
}
const {copyright, links = [], logo} = footer;
return (
<footer
className={classnames('footer', {
@ -43,20 +64,7 @@ function Footer() {
<ul className="footer__items">
{linkItem.items.map(item => (
<li key={item.href || item.to} className="footer__item">
<Link
className="footer__link-item"
{...item}
{...(item.href
? {
target: '_blank',
rel: 'noopener noreferrer',
href: item.href,
}
: {
to: withBaseUrl(item.to),
})}>
{item.label}
</Link>
<FooterLink item={item} />
</li>
))}
</ul>
@ -69,11 +77,7 @@ function Footer() {
<div className="text--center">
{logo && logo.src && (
<div className="margin-bottom--sm">
<img
className="footer__logo"
alt={logo.alt}
src={withBaseUrl(logo.src)}
/>
<img className="footer__logo" alt={logo.alt} src={logoUrl} />
</div>
)}
{copyright}

View file

@ -8,7 +8,7 @@
import React from 'react';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Navbar from '@theme/Navbar';
import Footer from '@theme/Footer';
@ -34,6 +34,8 @@ function Layout(props) {
} = props;
const metaTitle = title || `${defaultTitle} · ${tagline}`;
const metaImage = image || defaultImage;
const metaImageUrl = siteUrl + useBaseUrl(metaImage);
const faviconUrl = useBaseUrl(favicon);
return (
<React.Fragment>
<Head>
@ -42,7 +44,7 @@ function Layout(props) {
<meta name="viewport" content="width=device-width" />
{metaTitle && <title>{metaTitle}</title>}
{metaTitle && <meta property="og:title" content={metaTitle} />}
{favicon && <link rel="shortcut icon" href={withBaseUrl(favicon)} />}
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
{description && <meta name="description" content={description} />}
{description && (
<meta property="og:description" content={description} />
@ -50,18 +52,8 @@ function Layout(props) {
{keywords && keywords.length && (
<meta name="keywords" content={keywords.join(',')} />
)}
{metaImage && (
<meta
property="og:image"
content={siteUrl + withBaseUrl(metaImage)}
/>
)}
{metaImage && (
<meta
property="twitter:image"
content={siteUrl + withBaseUrl(metaImage)}
/>
)}
{metaImage && <meta property="og:image" content={metaImageUrl} />}
{metaImage && <meta property="twitter:image" content={metaImageUrl} />}
{metaImage && (
<meta name="twitter:image:alt" content={`Image for ${metaTitle}`} />
)}

View file

@ -11,7 +11,7 @@ import Toggle from 'react-toggle';
import Link from '@docusaurus/Link';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
import SearchBar from '@theme/SearchBar';
@ -20,6 +20,7 @@ import classnames from 'classnames';
import styles from './styles.module.css';
function NavLink(props) {
const toUrl = useBaseUrl(props.to);
return (
<Link
className="navbar__item navbar__link"
@ -32,7 +33,7 @@ function NavLink(props) {
}
: {
activeClassName: 'navbar__link--active',
to: withBaseUrl(props.to),
to: toUrl,
})}>
{props.label}
</Link>
@ -54,7 +55,7 @@ function Navbar() {
const {siteConfig = {}} = context;
const {baseUrl, themeConfig = {}} = siteConfig;
const {algolia, navbar = {}} = themeConfig;
const {title, logo, links = []} = navbar;
const {title, logo = {}, links = []} = navbar;
const showSidebar = useCallback(() => {
setSidebarShown(true);
@ -82,6 +83,7 @@ function Navbar() {
}
};
const logoUrl = useBaseUrl(logo.src);
return (
<React.Fragment>
<Head>
@ -120,11 +122,7 @@ function Navbar() {
</div>
<Link className="navbar__brand" to={baseUrl}>
{logo != null && (
<img
className="navbar__logo"
src={withBaseUrl(logo.src)}
alt={logo.alt}
/>
<img className="navbar__logo" src={logoUrl} alt={logo.alt} />
)}
{title != null && (
<strong
@ -176,11 +174,7 @@ function Navbar() {
<div className="navbar__sidebar__brand">
<Link className="navbar__brand" onClick={hideSidebar} to={baseUrl}>
{logo != null && (
<img
className="navbar__logo"
src={withBaseUrl(logo.src)}
alt={logo.alt}
/>
<img className="navbar__logo" src={logoUrl} alt={logo.alt} />
)}
{title != null && <strong>{title}</strong>}
</Link>

View file

@ -0,0 +1,52 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import useBaseUrl from '../useBaseUrl';
import useDocusaurusContext from '../useDocusaurusContext';
jest.mock('../useDocusaurusContext', () => jest.fn(), {virtual: true});
const mockedContext = <jest.Mock>useDocusaurusContext;
describe('useBaseUrl', () => {
test('empty base URL', () => {
mockedContext.mockImplementation(() => ({
siteConfig: {
baseUrl: '/',
},
}));
expect(useBaseUrl('hello')).toEqual('/hello');
expect(useBaseUrl('/hello')).toEqual('/hello');
expect(useBaseUrl('hello/')).toEqual('/hello/');
expect(useBaseUrl('/hello/')).toEqual('/hello/');
expect(useBaseUrl('hello/byebye')).toEqual('/hello/byebye');
expect(useBaseUrl('/hello/byebye')).toEqual('/hello/byebye');
expect(useBaseUrl('hello/byebye/')).toEqual('/hello/byebye/');
expect(useBaseUrl('/hello/byebye/')).toEqual('/hello/byebye/');
expect(useBaseUrl('https://github.com')).toEqual('https://github.com');
expect(useBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
});
test('non-empty base URL', () => {
mockedContext.mockImplementation(() => ({
siteConfig: {
baseUrl: '/docusaurus/',
},
}));
expect(useBaseUrl('hello')).toEqual('/docusaurus/hello');
expect(useBaseUrl('/hello')).toEqual('/docusaurus/hello');
expect(useBaseUrl('hello/')).toEqual('/docusaurus/hello/');
expect(useBaseUrl('/hello/')).toEqual('/docusaurus/hello/');
expect(useBaseUrl('hello/byebye')).toEqual('/docusaurus/hello/byebye');
expect(useBaseUrl('/hello/byebye')).toEqual('/docusaurus/hello/byebye');
expect(useBaseUrl('hello/byebye/')).toEqual('/docusaurus/hello/byebye/');
expect(useBaseUrl('/hello/byebye/')).toEqual('/docusaurus/hello/byebye/');
expect(useBaseUrl('https://github.com')).toEqual('https://github.com');
expect(useBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
});
});

View file

@ -1,52 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import withBaseUrl from '../withBaseUrl';
import useDocusaurusContext from '../useDocusaurusContext';
jest.mock('../useDocusaurusContext', () => jest.fn(), {virtual: true});
const mockedContext = <jest.Mock>useDocusaurusContext;
describe('withBaseUrl', () => {
test('empty base URL', () => {
mockedContext.mockImplementation(() => ({
siteConfig: {
baseUrl: '/',
},
}));
expect(withBaseUrl('hello')).toEqual('/hello');
expect(withBaseUrl('/hello')).toEqual('/hello');
expect(withBaseUrl('hello/')).toEqual('/hello/');
expect(withBaseUrl('/hello/')).toEqual('/hello/');
expect(withBaseUrl('hello/byebye')).toEqual('/hello/byebye');
expect(withBaseUrl('/hello/byebye')).toEqual('/hello/byebye');
expect(withBaseUrl('hello/byebye/')).toEqual('/hello/byebye/');
expect(withBaseUrl('/hello/byebye/')).toEqual('/hello/byebye/');
expect(withBaseUrl('https://github.com')).toEqual('https://github.com');
expect(withBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
});
test('non-empty base URL', () => {
mockedContext.mockImplementation(() => ({
siteConfig: {
baseUrl: '/docusaurus/',
},
}));
expect(withBaseUrl('hello')).toEqual('/docusaurus/hello');
expect(withBaseUrl('/hello')).toEqual('/docusaurus/hello');
expect(withBaseUrl('hello/')).toEqual('/docusaurus/hello/');
expect(withBaseUrl('/hello/')).toEqual('/docusaurus/hello/');
expect(withBaseUrl('hello/byebye')).toEqual('/docusaurus/hello/byebye');
expect(withBaseUrl('/hello/byebye')).toEqual('/docusaurus/hello/byebye');
expect(withBaseUrl('hello/byebye/')).toEqual('/docusaurus/hello/byebye/');
expect(withBaseUrl('/hello/byebye/')).toEqual('/docusaurus/hello/byebye/');
expect(withBaseUrl('https://github.com')).toEqual('https://github.com');
expect(withBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
});
});

View file

@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import * as React from 'react';
import {DocusaurusContext} from '@docusaurus/types';
import React from 'react';
export default React.createContext<DocusaurusContext>({});
export default React.createContext({});

View file

@ -7,10 +7,14 @@
import useDocusaurusContext from './useDocusaurusContext';
function withBaseUrl(url: string): string {
export default function useBaseUrl(url) {
const {siteConfig} = useDocusaurusContext();
const {baseUrl = '/'} = siteConfig || {};
if (!url) {
return url;
}
const externalRegex = /^(https?:|\/\/)/;
if (externalRegex.test(url)) {
return url;
@ -20,5 +24,3 @@ function withBaseUrl(url: string): string {
}
return baseUrl + url;
}
export default withBaseUrl;

View file

@ -7,9 +7,8 @@
import {useContext} from 'react';
import context from './context';
import {DocusaurusContext} from '@docusaurus/types';
function useDocusaurusContext(): DocusaurusContext {
function useDocusaurusContext() {
return useContext(context);
}

View file

@ -8,18 +8,19 @@
import React from 'react';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
function Layout(props) {
const context = useDocusaurusContext();
const {siteConfig = {}} = context;
const {favicon, tagline, title: defaultTitle} = siteConfig;
const {children, title, description} = props;
const faviconUrl = useBaseUrl(favicon);
return (
<React.Fragment>
<Head defaultTitle={`${defaultTitle} · ${tagline}`}>
{title && <title>{`${title} · ${tagline}`}</title>}
{favicon && <link rel="shortcut icon" href={withBaseUrl(favicon)} />}
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
{description && <meta name="description" content={description} />}
{description && (
<meta property="og:description" content={description} />

View file

@ -65,10 +65,27 @@ export function createBaseConfig(
parallel: true,
sourceMap: false,
terserOptions: {
ecma: 6,
mangle: true,
parse: {
// we want uglify-js to parse ecma 8 code. However, we don't want it
// to apply any minfication steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
}),

View file

@ -88,7 +88,21 @@ export function getBabelLoader(isServer: boolean, babelOptions?: {}): Loader {
{
babelrc: false,
configFile: false,
presets: ['@babel/env', '@babel/react'],
presets: [
[
'@babel/env',
{
useBuiltIns: 'usage',
loose: true,
corejs: '2',
// Do not transform modules to CJS
modules: false,
// Exclude transforms that make all code slower
exclude: ['transform-typeof-symbol'],
},
],
'@babel/react',
],
plugins: [
isServer ? 'dynamic-import-node' : '@babel/syntax-dynamic-import',
],