From 2bbfbf88d62db2def6ce09b6a2f0f955bba10aef Mon Sep 17 00:00:00 2001 From: Endi Date: Sat, 26 Oct 2019 15:10:48 +0700 Subject: [PATCH 1/3] docs(v2): update plugins, presets and themes docs (#1889) * docs(v2): update plugins, presets and themes docs * ideal image plugin * proof reading * Merge master --- packages/docusaurus-types/src/index.d.ts | 2 +- website/docs/advanced-plugins.md | 93 ++++++++++------- website/docs/advanced-presets.md | 17 ---- website/docs/advanced-themes.md | 45 +-------- website/docs/api-themes.md | 23 ----- website/docs/lifecycle-apis.md | 123 +++++++++++++++-------- website/docs/migrating-from-v1-to-v2.md | 2 +- website/docs/motivation.md | 9 -- website/docs/presets.md | 121 ++++++++++++++++++++++ website/docs/using-plugins.md | 52 +++------- website/docs/using-themes.md | 64 +++++++++++- website/sidebars.js | 22 ++-- 12 files changed, 353 insertions(+), 220 deletions(-) delete mode 100644 website/docs/advanced-presets.md delete mode 100644 website/docs/api-themes.md delete mode 100644 website/docs/motivation.md create mode 100644 website/docs/presets.md diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index 6793615bb3b2..54424e6335fd 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -94,7 +94,7 @@ export interface Plugin { getThemePath?(): string; getPathsToWatch?(): string[]; getClientModules?(): string[]; - extendCli?(cli: CommanderStatic): any; + extendCli?(cli: CommanderStatic): void; } export type PluginConfig = [string, Object] | [string] | string; diff --git a/website/docs/advanced-plugins.md b/website/docs/advanced-plugins.md index 3491ae5a8105..80ff73a11788 100644 --- a/website/docs/advanced-plugins.md +++ b/website/docs/advanced-plugins.md @@ -1,23 +1,16 @@ --- id: advanced-plugins -title: Plugins +title: Writing Plugins --- - - In this doc, we talk about the design intention of plugins and how you may write your own plugins. -Docusaurus Plugins are very similar to [Gatsby Plugins](https://www.gatsbyjs.org/plugins/) and [VuePress Plugins](https://v1.vuepress.vuejs.org/plugin/). The main difference here is that Docusaurus plugins don't allow using other plugins. Docusaurus provides [presets](./advanced-presets.md) to bundle plugins that are meant to work together. +Docusaurus Plugins are very similar to [Gatsby Plugins](https://www.gatsbyjs.org/plugins/) and [VuePress Plugins](https://v1.vuepress.vuejs.org/plugin/). The main difference here is that Docusaurus plugins don't allow plugins to include another plugin. Docusaurus provides [presets](presets.md) to bundle plugins that are meant to work together. ## Plugins design Docusaurus' implementation of the plugins system provides us with a convenient way to hook into the website's lifecycle to modify what goes on during development/build, which involves (but not limited to) extending the webpack config, modifying the data being loaded and creating new components to be used in a page. -In most cases, plugins are there to fetch data and create routes. A plugin could take in components as part of its options and to act as the wrapper for the page. A plugin can also provide React components to be used together with the non-UI functionality. You can also specify a resolution rule for the plugin to find its components to call, which you then supply with a [theme](./advanced-themes.md). - ## Creating plugins A plugin is a module which exports a function that takes two parameters and returns an object when executed. @@ -34,7 +27,7 @@ module.exports = function(context, options) { name: 'my-docusaurus-plugin', async loadContent() { ... }, async contentLoaded({content, actions}) { ... }, - ... + /* other lifecycle api */ }; }; ``` @@ -55,7 +48,7 @@ interface LoadContext { #### `options` -`options` are the [second optional parameter when the plugins are used](./using-plugins.md#configuring-plugins). `options` is plugin-specific and are specified by the user when they use it in `docusaurus.config.js` or if preset contains the plugin. The preset will then be in-charge of passing the correct options into the plugin. It is up to individual plugins to define what options it takes. +`options` are the [second optional parameter when the plugins are used](using-plugins.md#configuring-plugins). `options` are plugin-specific and are specified by users when they use them in `docusaurus.config.js`. Alternatively, if preset contains the plugin, the preset will then be in charge of passing the correct options into the plugin. It is up to individual plugin to define what options it takes. #### Return value @@ -67,7 +60,7 @@ Find the list of official Docusaurus plugins [here](https://github.com/facebook/ ### `@docusaurus/plugin-content-blog` -The default blog plugin for Docusaurus. The classic template ships with this plugin with default configurations. +Provides the [Blog](blog.md) feature and is the default blog plugin for Docusaurus. The classic template ships with this plugin with default configurations. ```js // docusaurus.config.js @@ -106,26 +99,9 @@ module.exports = { }; ``` - - ### `@docusaurus/plugin-content-docs` -The default docs plugin for Docusaurus. The classic template ships with this plugin with default configurations. +Provides the [Docs](markdown-features.mdx) functionality and is the default docs plugin for Docusaurus. The classic template ships with this plugin with default configurations. ```js // docusaurus.config.js @@ -166,11 +142,11 @@ module.exports = { rehypePlugins: [], /** * Whether to display the author who last updated the doc. - * / + */ showLastUpdateAuthor: false, /** * Whether to display the last date the doc was updated. - * / + */ showLastUpdateTime: false, }, ], @@ -180,7 +156,7 @@ module.exports = { ### `@docusaurus/plugin-content-pages` -The default pages plugin for Docusaurus. The classic template ships with this plugin with default configurations. +The default pages plugin for Docusaurus. The classic template ships with this plugin with default configurations. This plugin provides [creating pages](creating-pages.md) functionality. ```js // docusaurus.config.js @@ -214,7 +190,7 @@ The default [Google Analytics](https://developers.google.com/analytics/devguides **Installation** ```shell -$ yarn add @docusaurus/plugin-google-analytics +$ npm install --save @docusaurus/plugin-google-analytics ``` **Configuration** @@ -238,7 +214,7 @@ The default [Global Site Tag (gtag.js)](https://developers.google.com/analytics/ **Installation** ```shell -$ yarn add @docusaurus/plugin-google-gtag +$ npm install --save @docusaurus/plugin-google-gtag ``` **Configuration** @@ -257,7 +233,7 @@ module.exports = { ### `@docusaurus/plugin-sitemap` -The classic template ships with this plugin. +The classic template ships with this plugin. This plugin creates sitemap for your site so that search engine crawlers can crawl your site more accurately. ```js // docusaurus.config.js @@ -272,3 +248,48 @@ module.exports = { ], }; ``` + +### `@docusaurus/plugin-ideal-image` + +Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder) + +```sh +npm install --save @docusaurus/plugin-ideal-image +``` + +Modify your `docusaurus.config.js` + +```diff +module.exports = { + ... ++ plugins: ['@docusaurus/plugin-ideal-image'], + ... +} +``` + +## Usage + +This plugin supports png, gif and jpg only + +```jsx +import Image from '@theme/IdealImage'; +import thumbnail from './path/to/img.png'; + +// your react code + + +// or + +``` + +### Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `name` | `string` | `ideal-img/[name].[hash:hex:7].[width].[ext]` | Filename template for output files. | +| `sizes` | `array` | _original size_ | Specify all widths you want to use. If a specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up). You may also declare a default `sizes` array in the loader options in your `webpack.config.js`. | +| `size` | `integer` | _original size_ | Specify one width you want to use; if the specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up) | +| `min` | `integer` | | As an alternative to manually specifying `sizes`, you can specify `min`, `max` and `steps`, and the sizes will be generated for you. | +| `max` | `integer` | | See `min` above | +| `steps` | `integer` | `4` | Configure the number of images generated between `min` and `max` (inclusive) | +| `quality` | `integer` | `85` | JPEG compression quality | diff --git a/website/docs/advanced-presets.md b/website/docs/advanced-presets.md deleted file mode 100644 index b21907588e65..000000000000 --- a/website/docs/advanced-presets.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -id: advanced-presets -title: Presets ---- - -_This section is a work in progress._ - - diff --git a/website/docs/advanced-themes.md b/website/docs/advanced-themes.md index 41585bb587d5..fe5f555c66aa 100644 --- a/website/docs/advanced-themes.md +++ b/website/docs/advanced-themes.md @@ -1,6 +1,6 @@ --- id: advanced-themes -title: Themes +title: Writing Themes --- In this doc, we discuss how themes are designed and how you can write your own themes. @@ -32,47 +32,8 @@ A Docusaurus theme normally includes an `index.js` file where you hook up to the ``` There are two lifecycle methods that are essential to theme implementation: - -**`getThemePath`** - -Returns the path to the directory where the theme components can be found. When your users calls `swizzle`, `getThemePath` is called and its returned path is used to find your theme components. - -If you use the folder directory above, your `getThemePath` can be: - -```js -// my-theme/src/index.js - -const path = require('path'); -module.exports = function(context, options) { - return { - name: 'name-of-my-theme', - getThemePath() { - return path.resolve(__dirname, './theme'); - }, - }; -}; -``` - -**`getClientModules`** - -Returns an array of paths to the modules that are to be imported in the client bundle. These modules are imported globally before React even renders the initial UI. - -As an example, to make your theme load a `customCss` object from `options` passed in by the user: - -```js -// my-theme/src/index.js - -const path = require('path'); -module.exports = function(context, options) { - const {customCss} = options || {}; - return { - name: 'name-of-my-theme', - getClientModules() { - return [customCss]; - }, - }; -}; -``` +- [getThemePath](lifecycle-apis.md#getthemepath) +- [getClientModules](lifecycle-apis.md#getclientmodules) diff --git a/website/docs/lifecycle-apis.md b/website/docs/lifecycle-apis.md index 70e0431ff417..c7d5c70d83e7 100644 --- a/website/docs/lifecycle-apis.md +++ b/website/docs/lifecycle-apis.md @@ -7,7 +7,7 @@ _This section is a work in progress._ Lifecycle APIs are shared by Themes and Plugins. -## `getPathsToWatch(): string[]` +## getPathsToWatch() Specifies the paths to watch for plugins and themes. The paths are watched by the dev server so that the plugin lifecycles are reloaded when contents in the watched paths change. Note that the plugins and themes modules are initially called with `context` and `options` from Node, which you may use to find the necessary directory information about the site. @@ -21,11 +21,11 @@ getPathsToWatch() { } ``` -## `async loadContent()` +## async loadContent() Plugins should use this lifecycle to fetch from data sources (filesystem, remote API, headless CMS, etc). -## `async contentLoaded({content, actions})` +## async contentLoaded({content, actions}) Plugins should use the data loaded in `loadContent` and construct the pages/routes that consume the loaded data. @@ -60,21 +60,15 @@ Example `addRoute` call: ```js addRoute({ - path: permalink, - component: blogPostComponent, + path: '/help', + component: '@site/src/pages/help', exact: true, - modules: { - content: source, - metadata: metadataPath, - prevItem: prevItem && prevItem.metadataPath, - nextItem: nextItem && nextItem.metadataPath, - }, }); ``` And `createData` takes a file name relative to to your plugin's directory, a string for the `JSON.stringify` result of your data, and will return a path to the module which you may then use as the path to items in your `RouteModule`. The modules will be loaded when the related pages are loaded following our optimizations according to the [PRPL pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/). -## `configureWebpack(config, isServer, utils)` +## configureWebpack(config, isServer, utils) Modifies the internal webpack config. If the return value is a JavaScript object, it will be merged into the final config using [`webpack-merge`](https://github.com/survivejs/webpack-merge). If it is a function, it will be called and receive `config` as the first argument and an `isServer` flag as the argument argument. @@ -99,34 +93,42 @@ You may use them to return your webpack configures conditionally. Example: ```js -configureWebpack(config, isServer, {getBabelLoader, getCacheLoader}) { - const {rehypePlugins, remarkPlugins, truncateMarker} = options; - return { - module: { - rules: [ - { - test: /(\.mdx?)$/, - use: [ - getCacheLoader(isServer), - getBabelLoader(isServer), - { - loader: '@docusaurus/mdx-loader', - options: { - remarkPlugins, - rehypePlugins, - }, - }, - { - loader: path.resolve(__dirname, './markdownLoader.js'), - options: { - truncateMarker, - }, - }, - ].filter(Boolean), - }, - ], - }, - }; +configureWebpack(config, isServer) { + if (!isServer) { + // mutate the webpack config for client + + } + return config; +}, +``` + +## postBuild(props) + +Called when a (production) build finishes. + +```ts +interface LoadContext { + siteDir: string; + generatedFilesDir: string; + siteConfig: DocusaurusConfig; + outDir: string; + baseUrl: string; +} + +interface Props extends LoadContext { + routesPaths: string[]; + plugins: Plugin[]; +} +``` + +Example: + +```js +async postBuild({siteConfig = {}, routesPaths = [], outDir}) { + // Print out to console all the rendered routes + routesPaths.map(route => { + console.log(route); + }) }, ``` @@ -147,6 +149,47 @@ extendCli(cli) { }, ``` +## getThemePath() + +Returns the path to the directory where the theme components can be found. When your users calls `swizzle`, `getThemePath` is called and its returned path is used to find your theme components. + +If you use the folder directory above, your `getThemePath` can be: + +```js +// my-theme/src/index.js + +const path = require('path'); +module.exports = function(context, options) { + return { + name: 'name-of-my-theme', + getThemePath() { + return path.resolve(__dirname, './theme'); + }, + }; +}; +``` + +## getClientModules() + +Returns an array of paths to the modules that are to be imported in the client bundle. These modules are imported globally before React even renders the initial UI. + +As an example, to make your theme load a `customCss` object from `options` passed in by the user: + +```js +// my-theme/src/index.js + +const path = require('path'); +module.exports = function(context, options) { + const {customCss} = options || {}; + return { + name: 'name-of-my-theme', + getClientModules() { + return [customCss]; + }, + }; +}; +``` + diff --git a/website/docs/using-plugins.md b/website/docs/using-plugins.md index 95aa466cec1a..481083d1d86c 100644 --- a/website/docs/using-plugins.md +++ b/website/docs/using-plugins.md @@ -1,18 +1,16 @@ --- id: using-plugins -title: Using Plugins -description: A plugin is a package that exports a class which can be instantiated with configurable options (provided by the user) and its various lifecycle methods will be invoked by the Docusaurus runtime. +title: Plugins +sidebar_label: Introduction --- -Plugins are the building blocks which add features to a Docusaurus 2 site. Each plugin handles its own individual feature. Plugins may work be bundled together and distributed via [presets](advanced-presets.md). - -Docusaurus 2 provides a few essential plugins such as [Google Analytics](advanced-plugins.md#docusaurusplugin-google-analytics) and [Sitemap](advanced-plugins.md#docusaurusplugin-sitemap). You may also write your own plugins for customized features. +Plugins are the building blocks of features in a Docusaurus 2 site. Each plugin handles its own individual feature. Plugins may work and be distributed as part of bundle via [presets](presets.md). In this doc, we talk about how to use plugins with Docusaurus' official plugins. To learn about the design implementation and how to write your own plugins, check out [Advanced Guides: Plugins](advanced-plugins.md). ## Installing a plugin -A plugin is an npm package, so you install them like other npm packages using npm. +A plugin is usually a npm package, so you install them like other npm packages using npm. ```bash npm install --save docusaurus-plugin-name @@ -40,58 +38,40 @@ module.exports = { ## Configuring plugins -To use a plugin, add the plugin to the `plugins` field of your `docusaurus.config.js`. +For the most basic usage of plugins, you can provide just the plugin name or the absolute path to the plugin. -For the most basic usage of plugins, you can providing just the plugin name or the absolute path to the plugin. For plugins that require options, specify the plugin as an array where the first value is the plugin name/path and second value is an options object, e.g. `['plugin-name', { path: 'foo/bar' }]` array. +However, plugins can have options specified by wrapping the name and an options object in an array inside your config. This style is usually called `Babel Style`. ```js // docusaurus.config.js module.exports = { plugins: [ - // Basic usage. - '@docusaurus/plugin-google-analytics', - - // With options object. [ - '@docusaurus/plugin-sitemap', + '@docusaurus/plugin-xxx', { - cacheTime: 600 * 1000, + /* options */ }, ], ], }; ``` -## Passing options to Docusaurus plugins via preset - -If you initialized your site using the classic template, you do not have to specify plugin options individually in your `docusaurus.config.js`. To provide the neccesary fields to certain plugins, i.e. `customCss` for `@docusaurus/theme-classic`, pass them in the preset field, like this: +Example: ```js // docusaurus.config.js module.exports = { - presets: [ + plugins: [ + // Basic usage. + '@docusaurus/plugin-google-analytics', + + // With options object (babel style) [ - '@docusaurus/preset-classic', + '@docusaurus/plugin-sitemap', { - // Will be passed to @docusaurus/theme-classic. - theme: { - customCss: require.resolve('./src/css/custom.css'), - }, - ... + cacheTime: 600 * 1000, }, ], ], }; ``` - -## Official plugins by Docusaurus - -Docusaurus' classic template is scaffolded with the classic preset, which includes the following official plugins. You may read more about the configurations of these plugins in our [Advanced Guides: Plugins](advanced-plugins.md). - -- [@docusaurus/plugin-content-blog](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-content-blog) -- [@docusaurus/plugin-content-docs](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-content-docs-legacy) -- [@docusaurus/plugin-content-pages](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-content-pages) -- [@docusaurus/plugin-google-analytics](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-google-analytics) -- [@docusaurus/plugin-google-gtag](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-google-gtag) -- [@docusaurus/plugin-sitemap](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-sitemap) -- [@docusaurus/plugin-ideal-image](https://github.com/facebook/docusaurus/tree/master/packages/docusaurus-plugin-ideal-image) diff --git a/website/docs/using-themes.md b/website/docs/using-themes.md index 760d39cc7886..0a9e38603087 100644 --- a/website/docs/using-themes.md +++ b/website/docs/using-themes.md @@ -1,11 +1,10 @@ --- id: using-themes -title: Using Themes +title: Themes +sidebar_label: Introduction --- -In Docusaurus 2, themes are there to finish the build step of your site by supplying the components used by your site, your plugins, and the themes themselves. Furthermore, you may easily swap out components from themes by _swizzling_ them with your own components. - -In this document, we discuss the basic usages of themes. You will learn how to use a theme and how to swizzle a component. To grasp a deeper understanding of themes, and / or to learn how you may implement your own themes, check out our [advanced guide on themes](advanced-themes.md). +Like plugins, themes are designed to add functionality to your Docusaurus site. As a good rule of thumb, themes are mostly focused on client-side, where plugins are more focused on server-side functionalities. Themes are also designed to be easily replace-able with other themes. ## Using themes @@ -18,9 +17,64 @@ module.exports = { }; ``` +## Theme components + +Most of the time, theme is used to provide a set of React components, e.g. `Navbar`, `Layout`, `Footer`. + +Users can use these components in their code by importing them using the `@theme` webpack alias: + +```js +import Navbar from '@theme/Navbar'; +``` + +The alias `@theme` can refer to a few directories, in the following priority: + +1. A user's `website/src/theme` directory, which is a special directory that has the higher precedence. +1. A Docusaurus theme packages's `theme` directory. +1. Fallback components provided by Docusaurus core (usually not needed). + +Given the following structure + +``` +website +├── node_modules +│ └── docusaurus-theme +│ └── theme +│ └── Navbar.js +└── src + └── theme + └── Navbar.js +``` + +`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. In iOS, method swizzling is the process of changing the implementation of an existing selector (method). In the context of a website, component swizzling means providing an alternative component that takes precedence over the component provided by the theme. + +**Themes are for providing UI components to present the content.** Most content plugins need to be paired with a theme in order to be actually useful. The UI is a separate layer from the data schema, so it makes it easy to swap out the themes for other designs (i.e., Bootstrap). + +For example, a Docusaurus blog consists of a blog plugin and a blog theme. + +```js +// docusaurus.config.js +{ + theme: ['theme-blog'], + plugins: ['plugin-content-blog'], +} +``` + +and if you want to use Bootstrap styling, you can swap out the theme with `theme-blog-bootstrap`: + +```js +// docusaurus.config.js +{ + theme: ['theme-blog-bootstrap'], + plugins: ['plugin-content-blog'], +} +``` + +The content plugin remains the same and the only thing you need to change is the theme. + ## Swizzling theme components -Themes are all about components. Docusaurus Themes' components are designed to be easily replaceable. We created a command for you to replace the components called `swizzle`. +Docusaurus Themes' components are designed to be easily replaceable. To make it easier for you, we created a command for you to replace theme components called `swizzle`. To swizzle a component for a theme, run the following command in your doc site: diff --git a/website/sidebars.js b/website/sidebars.js index 54d56c11cc48..cc54508511ef 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -7,11 +7,7 @@ module.exports = { docs: { - Docusaurus: [ - 'introduction', - /*'motivation', */ 'design-principles', - 'contributing', - ], + Docusaurus: ['introduction', 'design-principles', 'contributing'], 'Getting Started': ['installation', 'configuration'], Guides: [ 'creating-pages', @@ -26,15 +22,21 @@ module.exports = { 'analytics', 'seo', 'search', - 'using-plugins', - 'using-themes', 'deployment', 'migrating-from-v1-to-v2', ], 'Advanced Guides': [ - 'advanced-plugins', - 'advanced-themes', - 'advanced-presets', + { + type: 'category', + label: 'Plugins', + items: ['using-plugins', 'advanced-plugins'], + }, + { + type: 'category', + label: 'Themes', + items: ['using-themes', 'advanced-themes'], + }, + 'presets', ], 'API Reference': [ 'cli', From c23f981f679d669f319e70d8f62def1a13c0afed Mon Sep 17 00:00:00 2001 From: Pawel Kadluczka Date: Sun, 27 Oct 2019 00:44:53 -0700 Subject: [PATCH 2/3] refactor(v2): Convert sitemap plugin to TypeScript (#1894) * Convert sitemap plugin to TypeScript Test - enabled the sitemap plugin in the v2 website and verified that the sitemap is created after running `docusaurus build`. * Addressing review comments --- .eslintignore | 1 + .gitignore | 1 + .prettierignore | 1 + CHANGELOG-2.x.md | 2 ++ .../docusaurus-plugin-sitemap/package.json | 6 +++- ...eSitemap.test.js => createSitemap.test.ts} | 19 ++++++---- .../src/createSitemap.js | 33 ----------------- .../src/createSitemap.ts | 36 +++++++++++++++++++ .../src/{index.js => index.ts} | 24 +++++++------ .../docusaurus-plugin-sitemap/src/types.ts | 12 +++++++ .../docusaurus-plugin-sitemap/tsconfig.json | 9 +++++ 11 files changed, 94 insertions(+), 50 deletions(-) rename packages/docusaurus-plugin-sitemap/src/__tests__/{createSitemap.test.js => createSitemap.test.ts} (70%) delete mode 100644 packages/docusaurus-plugin-sitemap/src/createSitemap.js create mode 100644 packages/docusaurus-plugin-sitemap/src/createSitemap.ts rename packages/docusaurus-plugin-sitemap/src/{index.js => index.ts} (61%) create mode 100644 packages/docusaurus-plugin-sitemap/src/types.ts create mode 100644 packages/docusaurus-plugin-sitemap/tsconfig.json diff --git a/.eslintignore b/.eslintignore index bda9362a870f..a8ca421a6ce2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -16,3 +16,4 @@ packages/docusaurus-init/lib/ packages/docusaurus-plugin-content-blog/lib/ packages/docusaurus-plugin-content-docs/lib/ packages/docusaurus-plugin-content-pages/lib/ +packages/docusaurus-plugin-sitemap/lib/ diff --git a/.gitignore b/.gitignore index 929727c2751f..e230c5dcf9a5 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ packages/docusaurus-init/lib/ packages/docusaurus-plugin-content-blog/lib/ packages/docusaurus-plugin-content-docs/lib/ packages/docusaurus-plugin-content-pages/lib/ +packages/docusaurus-plugin-sitemap/lib/ diff --git a/.prettierignore b/.prettierignore index 7499928e82e3..a74d4b9bb16f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,3 +8,4 @@ packages/docusaurus-init/lib/ packages/docusaurus-plugin-content-blog/lib/ packages/docusaurus-plugin-content-docs/lib/ packages/docusaurus-plugin-content-pages/lib/ +packages/docusaurus-plugin-sitemap/lib/ \ No newline at end of file diff --git a/CHANGELOG-2.x.md b/CHANGELOG-2.x.md index 98889efbae93..b6cec6facd92 100644 --- a/CHANGELOG-2.x.md +++ b/CHANGELOG-2.x.md @@ -2,6 +2,8 @@ ## Unreleased +- Convert sitemap plugin to TypeScript + ## 2.0.0-alpha.31 - Footer is now sticky/ pinned to the bottom of the viewport in desktop browsers. diff --git a/packages/docusaurus-plugin-sitemap/package.json b/packages/docusaurus-plugin-sitemap/package.json index af6761849d76..ee12645ea186 100644 --- a/packages/docusaurus-plugin-sitemap/package.json +++ b/packages/docusaurus-plugin-sitemap/package.json @@ -2,12 +2,16 @@ "name": "@docusaurus/plugin-sitemap", "version": "2.0.0-alpha.31", "description": "Simple sitemap generation plugin for Docusaurus", - "main": "src/index.js", + "main": "lib/index.js", + "scripts": { + "tsc": "tsc" + }, "publishConfig": { "access": "public" }, "license": "MIT", "dependencies": { + "@docusaurus/types": "^2.0.0-alpha.30", "sitemap": "^3.2.2" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-sitemap/src/__tests__/createSitemap.test.js b/packages/docusaurus-plugin-sitemap/src/__tests__/createSitemap.test.ts similarity index 70% rename from packages/docusaurus-plugin-sitemap/src/__tests__/createSitemap.test.js rename to packages/docusaurus-plugin-sitemap/src/__tests__/createSitemap.test.ts index f5cf466f0940..1f849716fc6b 100644 --- a/packages/docusaurus-plugin-sitemap/src/__tests__/createSitemap.test.js +++ b/packages/docusaurus-plugin-sitemap/src/__tests__/createSitemap.test.ts @@ -6,23 +6,30 @@ */ import createSitemap from '../createSitemap'; +import {DocusaurusConfig} from '@docusaurus/types'; +import DEFAULT_OPTIONS from '../index'; describe('createSitemap', () => { test('simple site', () => { - const sitemap = createSitemap({ - siteConfig: { + const sitemap = createSitemap( + { url: 'https://example.com', + } as DocusaurusConfig, + ['/', '/test'], + { + cacheTime: 600, + changefreq: 'daily', + priority: 0.7, }, - routesPaths: ['/', '/test'], - }); - expect(sitemap).toContain( + ); + expect(sitemap.toString()).toContain( ``, ); }); test('empty site', () => { expect(() => { - createSitemap({}); + createSitemap({} as any, [], {} as any); }).toThrowErrorMatchingInlineSnapshot( `"Url in docusaurus.config.js cannot be empty/undefined"`, ); diff --git a/packages/docusaurus-plugin-sitemap/src/createSitemap.js b/packages/docusaurus-plugin-sitemap/src/createSitemap.js deleted file mode 100644 index f9cc6318007e..000000000000 --- a/packages/docusaurus-plugin-sitemap/src/createSitemap.js +++ /dev/null @@ -1,33 +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. - */ - -const sitemap = require('sitemap'); - -module.exports = function createSitemap({ - siteConfig = {}, - routesPaths, - options = {}, -}) { - const {url: hostname} = siteConfig; - if (!hostname) { - throw new Error('Url in docusaurus.config.js cannot be empty/undefined'); - } - - const urls = routesPaths.map(routesPath => ({ - url: routesPath, - changefreq: options.changefreq, - priority: options.priority, - })); - - return sitemap - .createSitemap({ - hostname, - cacheTime: options.cacheTime, - urls, - }) - .toString(); -}; diff --git a/packages/docusaurus-plugin-sitemap/src/createSitemap.ts b/packages/docusaurus-plugin-sitemap/src/createSitemap.ts new file mode 100644 index 000000000000..50aa2be44e7c --- /dev/null +++ b/packages/docusaurus-plugin-sitemap/src/createSitemap.ts @@ -0,0 +1,36 @@ +/** + * 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 sitemap, {SitemapItemOptions} from 'sitemap'; +import {PluginOptions} from './types'; +import {DocusaurusConfig} from '@docusaurus/types'; + +export default function createSitemap( + siteConfig: DocusaurusConfig, + routesPaths: string[], + options: PluginOptions, +) { + const {url: hostname} = siteConfig; + if (!hostname) { + throw new Error('Url in docusaurus.config.js cannot be empty/undefined'); + } + + const urls = routesPaths.map( + routesPath => + ({ + url: routesPath, + changefreq: options.changefreq, + priority: options.priority, + } as SitemapItemOptions), + ); + + return sitemap.createSitemap({ + hostname, + cacheTime: options.cacheTime, + urls, + }); +} diff --git a/packages/docusaurus-plugin-sitemap/src/index.js b/packages/docusaurus-plugin-sitemap/src/index.ts similarity index 61% rename from packages/docusaurus-plugin-sitemap/src/index.js rename to packages/docusaurus-plugin-sitemap/src/index.ts index eb43d8338480..0394d40eb08a 100644 --- a/packages/docusaurus-plugin-sitemap/src/index.js +++ b/packages/docusaurus-plugin-sitemap/src/index.ts @@ -5,30 +5,34 @@ * LICENSE file in the root directory of this source tree. */ -const fs = require('fs'); -const path = require('path'); +import fs from 'fs'; +import path from 'path'; +import {PluginOptions} from './types'; +import createSitemap from './createSitemap'; +import {LoadContext, Props} from '@docusaurus/types'; -const createSitemap = require('./createSitemap'); - -const DEFAULT_OPTIONS = { +const DEFAULT_OPTIONS: PluginOptions = { cacheTime: 600 * 1000, // 600 sec - cache purge period changefreq: 'weekly', priority: 0.5, }; -module.exports = function(context, opts) { +export default function pluginSitemap( + _context: LoadContext, + opts: Partial, +) { const options = {...DEFAULT_OPTIONS, ...opts}; return { name: 'docusaurus-plugin-sitemap', - async postBuild({siteConfig = {}, routesPaths = [], outDir}) { + async postBuild({siteConfig, routesPaths, outDir}: Props) { // Generate sitemap - const generatedSitemap = createSitemap({ + const generatedSitemap = createSitemap( siteConfig, routesPaths, options, - }).toString(); + ).toString(); // Write sitemap file const sitemapPath = path.join(outDir, 'sitemap.xml'); @@ -39,4 +43,4 @@ module.exports = function(context, opts) { }); }, }; -}; +} diff --git a/packages/docusaurus-plugin-sitemap/src/types.ts b/packages/docusaurus-plugin-sitemap/src/types.ts new file mode 100644 index 000000000000..418f15aa0f0a --- /dev/null +++ b/packages/docusaurus-plugin-sitemap/src/types.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ + +export interface PluginOptions { + cacheTime: number; + changefreq: string; + priority: number; +} diff --git a/packages/docusaurus-plugin-sitemap/tsconfig.json b/packages/docusaurus-plugin-sitemap/tsconfig.json new file mode 100644 index 000000000000..f5902ba1089b --- /dev/null +++ b/packages/docusaurus-plugin-sitemap/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": "./lib/.tsbuildinfo", + "rootDir": "src", + "outDir": "lib" + } +} From fabaf7772b83cce0abc4f0433d4edddf74d3303f Mon Sep 17 00:00:00 2001 From: Endi Date: Sun, 27 Oct 2019 21:09:19 +0700 Subject: [PATCH 3/3] perf(v2): significantly reduce bundle size & initial html payload (#1898) * perf(v2): reduce bundle size significantly with super short chunk name and registry * changelog * use hash:8 as id for better long term caching * even shorter filename. slice contenthash --- CHANGELOG-2.x.md | 1 + .../src/__tests__/index.test.ts | 14 ++++++++++++ packages/docusaurus-utils/src/index.ts | 22 +++++++++++++------ .../src/client/exports/ComponentCreator.js | 8 ++++--- packages/docusaurus/src/server/index.ts | 9 ++++---- packages/docusaurus/src/server/routes.ts | 3 ++- packages/docusaurus/src/webpack/base.ts | 8 +++---- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/CHANGELOG-2.x.md b/CHANGELOG-2.x.md index b6cec6facd92..5ba3a327a42b 100644 --- a/CHANGELOG-2.x.md +++ b/CHANGELOG-2.x.md @@ -3,6 +3,7 @@ ## Unreleased - Convert sitemap plugin to TypeScript +- Significantly reduce main bundle size and initial HTML payload on production build. Generated JS files from webpack is also shorter in name. ## 2.0.0-alpha.31 diff --git a/packages/docusaurus-utils/src/__tests__/index.test.ts b/packages/docusaurus-utils/src/__tests__/index.test.ts index 464b6587bcd6..2c0b770b1749 100644 --- a/packages/docusaurus-utils/src/__tests__/index.test.ts +++ b/packages/docusaurus-utils/src/__tests__/index.test.ts @@ -108,6 +108,20 @@ describe('load utils', () => { Object.keys(secondAssert).forEach(str => { expect(genChunkName(str, undefined, 'blog')).toBe(secondAssert[str]); }); + + // Only generate short unique id + const thirdAssert = { + a: '0cc175b9', + b: '92eb5ffe', + c: '4a8a08f0', + d: '8277e091', + }; + Object.keys(thirdAssert).forEach(str => { + expect(genChunkName(str, undefined, undefined, true)).toBe( + thirdAssert[str], + ); + }); + expect(genChunkName('d', undefined, undefined, true)).toBe('8277e091'); }); test('idx', () => { diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index ccc87cd2d8d0..01c73cdd368f 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -101,19 +101,27 @@ export function genChunkName( modulePath: string, prefix?: string, preferredName?: string, + shortId?: boolean, ): string { let chunkName: string | undefined = chunkNameCache.get(modulePath); if (!chunkName) { - let str = modulePath; - if (preferredName) { - const shortHash = createHash('md5') + if (shortId) { + chunkName = createHash('md5') .update(modulePath) .digest('hex') - .substr(0, 3); - str = `${preferredName}${shortHash}`; + .substr(0, 8); + } else { + let str = modulePath; + if (preferredName) { + const shortHash = createHash('md5') + .update(modulePath) + .digest('hex') + .substr(0, 3); + str = `${preferredName}${shortHash}`; + } + const name = str === '/' ? 'index' : docuHash(str); + chunkName = prefix ? `${prefix}---${name}` : name; } - const name = str === '/' ? 'index' : docuHash(str); - chunkName = prefix ? `${prefix}---${name}` : name; chunkNameCache.set(modulePath, chunkName); } return chunkName; diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.js b/packages/docusaurus/src/client/exports/ComponentCreator.js index 46ab1b15dcf4..d82095d67021 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.js +++ b/packages/docusaurus/src/client/exports/ComponentCreator.js @@ -56,9 +56,11 @@ function ComponentCreator(path) { } const chunkRegistry = registry[target] || {}; - optsLoader[keys.join('.')] = chunkRegistry.loader; - optsModules.push(chunkRegistry.module); - optsWebpack.push(chunkRegistry.webpack); + /* eslint-disable prefer-destructuring */ + optsLoader[keys.join('.')] = chunkRegistry[0]; + optsModules.push(chunkRegistry[1]); + optsWebpack.push(chunkRegistry[2]); + /* eslint-enable prefer-destructuring */ } traverseChunk(chunkNames, []); diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index 0b9270e4ac51..f97654b9a37e 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -117,11 +117,10 @@ export async function load(siteDir: string): Promise { `export default { ${Object.keys(registry) .map( - key => ` '${key}': { - 'loader': ${registry[key].loader}, - 'module': ${JSON.stringify(registry[key].modulePath)}, - 'webpack': require.resolveWeak(${JSON.stringify(registry[key].modulePath)}), - },`, + key => + ` '${key}': [${registry[key].loader}, ${JSON.stringify( + registry[key].modulePath, + )}, require.resolveWeak(${JSON.stringify(registry[key].modulePath)})],`, ) .join('\n')}};\n`, ); diff --git a/packages/docusaurus/src/server/routes.ts b/packages/docusaurus/src/server/routes.ts index 520f22a9e0a2..6f2bfa86194d 100644 --- a/packages/docusaurus/src/server/routes.ts +++ b/packages/docusaurus/src/server/routes.ts @@ -24,6 +24,7 @@ function getModulePath(target: Module): string { } export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) { + const isProd = process.env.NODE_ENV === 'production'; const routesImports = [ `import React from 'react';`, `import ComponentCreator from '@docusaurus/ComponentCreator';`, @@ -82,7 +83,7 @@ export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) { } const modulePath = getModulePath(value as Module); - const chunkName = genChunkName(modulePath, prefix, name); + const chunkName = genChunkName(modulePath, prefix, name, isProd); const loader = `() => import(/* webpackChunkName: '${chunkName}' */ ${JSON.stringify( modulePath, )})`; diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 866071e858de..bbbca0c38381 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -32,8 +32,8 @@ export function createBaseConfig( output: { pathinfo: false, path: outDir, - filename: isProd ? '[name].[contenthash].js' : '[name].js', - chunkFilename: isProd ? '[name].[contenthash].js' : '[name].js', + filename: isProd ? '[name].[contenthash:8].js' : '[name].js', + chunkFilename: isProd ? '[name].[contenthash:8].js' : '[name].js', publicPath: baseUrl, }, // Don't throw warning when asset created is over 250kb @@ -157,8 +157,8 @@ export function createBaseConfig( }, plugins: [ new MiniCssExtractPlugin({ - filename: isProd ? '[name].[contenthash].css' : '[name].css', - chunkFilename: isProd ? '[name].[contenthash].css' : '[name].css', + filename: isProd ? '[name].[contenthash:8].css' : '[name].css', + chunkFilename: isProd ? '[name].[contenthash:8].css' : '[name].css', }), ], };