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

@ -8,6 +8,9 @@
- Fix `swizzle` command not being able to swizzle single js file.
- Fix logo URL in footer to be appended with baseUrl automatically.
- Add the option `--no-open` for `start` command.
- Set `@babel/env` useBuiltins to `usage`. This will automatically use browserlist and import polyfills required.
- Modified TerserWebpackPlugin `terserOptions` for better cross-browser compatibility.
- **BREAKING** `withBaseUrl` is renamed to `useBaseUrl` because its a React Hooks. Make sure you import/rename it correctly. Eg: `import useBaseUrl from '@docusaurus/useBaseUrl`;
- Fix potential security vulnerability because we're exposing the directory structure of the host machine.
## 2.0.0-alpha.27

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',
],

View file

@ -13,23 +13,23 @@ This means that if the site's `baseUrl` is `/`, an image in `/static/img/docusau
You can reference assets from the `static` folder in your code. You could use hardcoded absolute paths, i.e. starting with a slash /, but remember to include the `baseUrl` if it is not `/`. However, this will break if you change your `baseUrl` in the config.
A better way would be to use the `withBaseUrl` utility function which appends the `baseUrl` to paths for you.
A better way would be to use the `useBaseUrl` utility function which appends the `baseUrl` to paths for you.
### JSX example
```jsx
// MyComponent.js
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
<img
alt="Docusaurus with Keytar"
src={withBaseUrl('img/docusaurus_keytar.svg')}
src={useBaseUrl('img/docusaurus_keytar.svg')}
/>;
```
### Markdown example
Thanks to MDX, you can also use `withBaseUrl` utility function in Markdown files! You'd have to use `<img>` tags instead of the Markdown image syntax though. The syntax is exactly the same as in JSX.
Thanks to MDX, you can also use `useBaseUrl` utility function in Markdown files! You'd have to use `<img>` tags instead of the Markdown image syntax though. The syntax is exactly the same as in JSX.
```txt
// my-doc.mdx
@ -38,11 +38,11 @@ id: my-doc
title: My Doc
---
import withBaseUrl from '@docusaurus/withBaseUrl'; // Add to the top of the file below the front matter.
import useBaseUrl from '@docusaurus/useBaseUrl'; // Add to the top of the file below the front matter.
...
<img alt="Docusaurus with Keytar" src={withBaseUrl('img/docusaurus_keytar.svg')} />;
<img alt="Docusaurus with Keytar" src={useBaseUrl('img/docusaurus_keytar.svg')} />;
```
You could also just use Markdown image syntax, but you would have to manually maintain the image paths yourself and isn't recommended.

View file

@ -9,7 +9,7 @@ import React, {useEffect} from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
function Help() {
return (
@ -27,7 +27,7 @@ function Help() {
<h2>Browse the docs</h2>
<p>
Learn more about Docusaurus using the{' '}
<Link to={withBaseUrl('docs/introduction')}>
<Link to={useBaseUrl('docs/introduction')}>
official documentation
</Link>
</p>

View file

@ -8,7 +8,7 @@
import React from 'react';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import withBaseUrl from '@docusaurus/withBaseUrl';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Image from '@theme/IdealImage';
import Layout from '@theme/Layout';
@ -73,7 +73,7 @@ function Home() {
<img
alt="Docusaurus with Keytar"
className={styles.heroLogo}
src={withBaseUrl('img/docusaurus_keytar.svg')}
src={useBaseUrl('img/docusaurus_keytar.svg')}
/>
{siteConfig.title} makes it easy to maintain{' '}
<span className={styles.heroProjectKeywords}>Open Source</span>{' '}
@ -82,7 +82,7 @@ function Home() {
<div className={styles.indexCtas}>
<Link
className={styles.indexCtasGetStartedButton}
to={withBaseUrl('docs/introduction')}>
to={useBaseUrl('docs/introduction')}>
Get Started
</Link>
<span className={styles.indexCtasGitHubButton}>
@ -105,14 +105,14 @@ function Home() {
Docusaurus 2
</a>
, contribute to its roadmap by suggesting features or giving feedback{' '}
<Link to={withBaseUrl('/feedback')}>here</Link>!
<Link to={useBaseUrl('/feedback')}>here</Link>!
</div>
</div>
<div
className={classnames(styles.announcement, styles.announcementPrimary)}>
<div className={styles.announcementInner}>
Coming from v1? Check out our{' '}
<Link to={withBaseUrl('/docs/migrating-from-v1-to-v2')}>
<Link to={useBaseUrl('/docs/migrating-from-v1-to-v2')}>
v1 to v2 migration guide
</Link>
.
@ -125,7 +125,7 @@ function Home() {
<img
className={styles.featureImage}
alt="Powered by MDX"
src={withBaseUrl('img/undraw_typewriter.svg')}
src={useBaseUrl('img/undraw_typewriter.svg')}
/>
<h3 className="padding-top--md">Powered by Markdown</h3>
<p className="padding-horiz--md">
@ -139,7 +139,7 @@ function Home() {
<img
alt="Built Using React"
className={styles.featureImage}
src={withBaseUrl('img/undraw_react.svg')}
src={useBaseUrl('img/undraw_react.svg')}
/>
<h3 className="padding-top--md">Built Using React</h3>
<p className="padding-horiz--md">
@ -152,7 +152,7 @@ function Home() {
<img
alt="Ready for Translations"
className={styles.featureImage}
src={withBaseUrl('img/undraw_around_the_world.svg')}
src={useBaseUrl('img/undraw_around_the_world.svg')}
/>
<h3 className="padding-top--md">Ready for Translations</h3>
<p className="padding-horiz--md">
@ -168,7 +168,7 @@ function Home() {
<img
alt="Document Versioning"
className={styles.featureImage}
src={withBaseUrl('img/undraw_version_control.svg')}
src={useBaseUrl('img/undraw_version_control.svg')}
/>
<h3 className="padding-top--md">Document Versioning</h3>
<p className="padding-horiz--md">
@ -181,7 +181,7 @@ function Home() {
<img
alt="Document Search"
className={styles.featureImage}
src={withBaseUrl('img/undraw_algolia.svg')}
src={useBaseUrl('img/undraw_algolia.svg')}
/>
<h3 className="padding-top--md">Document Search</h3>
<p className="padding-horiz--md">