feat(v2): implement client modules API (#1554)

* feat(v2): load client modules

* docs(v2): update plugins API

* misc(v2): change to import
This commit is contained in:
Yangshun Tay 2019-06-03 23:33:00 -07:00 committed by GitHub
parent 90138d52c2
commit c06ccc0a07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 174 additions and 3 deletions

View file

@ -8,12 +8,15 @@
import React from 'react';
import {renderRoutes} from 'react-router-config';
import Head from '@docusaurus/Head';
import routes from '@generated/routes';
import siteConfig from '@generated/docusaurus.config';
import Head from '@docusaurus/Head';
import DocusaurusContext from '@docusaurus/context';
import PendingNavigation from './PendingNavigation';
import '@generated/client-modules';
function App() {
return (
<DocusaurusContext.Provider value={{siteConfig}}>

View file

@ -0,0 +1,5 @@
module.exports = function() {
return {
name: 'plugin-empty',
};
};

View file

@ -0,0 +1,8 @@
module.exports = function() {
return {
name: 'plugin-foo-bar',
getClientModules() {
return ['foo', 'bar'];
},
};
};

View file

@ -0,0 +1,8 @@
module.exports = function() {
return {
plugin: 'plugin-hello-world',
getClientModules() {
return ['hello', 'world'];
},
};
};

View file

@ -0,0 +1,94 @@
/**
* 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 path from 'path';
import {loadClientModules} from '../index';
import {LoadContext} from '../../index';
const pluginEmpty = require('./__fixtures__/plugin-empty');
const pluginFooBar = require('./__fixtures__/plugin-foo-bar');
const pluginHelloWorld = require('./__fixtures__/plugin-hello-world');
describe('loadClientModules', () => {
test('empty', () => {
const clientModules = loadClientModules([pluginEmpty()]);
expect(clientModules).toMatchInlineSnapshot(`Array []`);
});
test('non-empty', () => {
const clientModules = loadClientModules([pluginFooBar()]);
expect(clientModules).toMatchInlineSnapshot(`
Array [
"foo",
"bar",
]
`);
});
test('multiple non-empty', () => {
const clientModules = loadClientModules([
pluginFooBar(),
pluginHelloWorld(),
]);
expect(clientModules).toMatchInlineSnapshot(`
Array [
"foo",
"bar",
"hello",
"world",
]
`);
});
test('multiple non-empty different order', () => {
const clientModules = loadClientModules([
pluginHelloWorld(),
pluginFooBar(),
]);
expect(clientModules).toMatchInlineSnapshot(`
Array [
"hello",
"world",
"foo",
"bar",
]
`);
});
test('empty and non-empty', () => {
const clientModules = loadClientModules([
pluginHelloWorld(),
pluginEmpty(),
pluginFooBar(),
]);
expect(clientModules).toMatchInlineSnapshot(`
Array [
"hello",
"world",
"foo",
"bar",
]
`);
});
test('empty and non-empty different order', () => {
const clientModules = loadClientModules([
pluginHelloWorld(),
pluginFooBar(),
pluginEmpty(),
]);
expect(clientModules).toMatchInlineSnapshot(`
Array [
"hello",
"world",
"foo",
"bar",
]
`);
});
});

View file

@ -0,0 +1,24 @@
/**
* 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 {Plugin} from '../plugins';
import _ from 'lodash';
export function loadClientModules(plugins: Plugin<any>[]): string[] {
return _.compact(
_.flatten<string | null>(
plugins.map(plugin => {
if (!plugin.getClientModules) {
return null;
}
return plugin.getClientModules();
}),
),
);
}

View file

@ -17,6 +17,8 @@ import {loadThemeAlias} from './themes';
import {loadPlugins} from './plugins';
import {loadRoutes} from './routes';
import {loadPresets} from './presets';
import {loadClientModules} from './client-modules';
import {GENERATED_FILES_DIR_NAME, CONFIG_FILE_NAME} from '../constants';
export interface CLIOptions {
@ -96,6 +98,16 @@ export async function load(
}),
});
// Load client modules.
const clientModules = loadClientModules(plugins);
const genClientModules = generate(
generatedFilesDir,
'client-modules.js',
`export default [\n${clientModules
.map(module => ` require(${JSON.stringify(module)}),`)
.join('\n')}\n];\n`,
);
// Routing
const {
registry,
@ -128,6 +140,7 @@ ${Object.keys(registry)
const genRoutes = generate(generatedFilesDir, 'routes.js', routesConfig);
await Promise.all([
genClientModules,
genSiteConfig,
genRegistry,
genRoutesChunkNames,

View file

@ -26,6 +26,7 @@ export interface Plugin<T> {
configureWebpack?(config: Configuration, isServer: boolean): Configuration;
getThemePath?(): string;
getPathsToWatch?(): string[];
getClientModules?(): string[];
}
export interface PluginConfig {

View file

@ -66,8 +66,12 @@ module.exports = function(context, opts) {
const options = {...DEFAULT_OPTIONS, ...options};
return {
// Namespace used for directories to cache the intermediate data for each plugin.
name: 'docusaurus-cool-plugin',
// A compulsory field used as the namespace for directories to cache
// the intermediate data for each plugin.
// If you're writing your own local plugin, you will want it to
// be unique in order not to potentially conflict with imported plugins.
// A good way will be to add your own project name within.
name: 'docusaurus-my-project-cool-plugin',
async loadContent() {
// The loadContent hook is executed after siteConfig and env has been loaded
@ -107,6 +111,17 @@ module.exports = function(context, opts) {
getPathsToWatch() {
// Path to watch
},
getThemePath() {
// Returns the path to the directory where the theme components can
// be found.
},
getClientModules() {
// Return 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.
},
};
};
```