mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-17 19:16:58 +02:00
feat(v2): implement ComponentCreator (#1366)
* v2(feat): convert blog to view-driven content queries * feat(v2): port blog to use ContentRenderer * misc(v2): fix test and change ContentRenderer url * avoid chunkName collision * avoid chunkname collision more * fix(v2): fix content-renderer ssr problem (#1367) * wip * avoid chunk names collision * ContentRenderer is a wrapper for Loadable * convert docs and pages * nits and rename * rename routeModules -> modules * remove lodash from component creator * resolve chunk not being picked up correctly * add comment for explanation * small refactoring
This commit is contained in:
parent
0ac2441d23
commit
96cb4672d5
14 changed files with 220 additions and 80 deletions
|
@ -45,6 +45,7 @@ module.exports = {
|
||||||
'jsx-a11y/click-events-have-key-events': OFF, // Revisit in future™
|
'jsx-a11y/click-events-have-key-events': OFF, // Revisit in future™
|
||||||
'jsx-a11y/no-noninteractive-element-interactions': OFF, // Revisit in future™
|
'jsx-a11y/no-noninteractive-element-interactions': OFF, // Revisit in future™
|
||||||
'no-console': OFF,
|
'no-console': OFF,
|
||||||
|
'no-underscore-dangle': OFF,
|
||||||
'react/jsx-closing-bracket-location': OFF, // Conflicts with Prettier.
|
'react/jsx-closing-bracket-location': OFF, // Conflicts with Prettier.
|
||||||
'react/jsx-filename-extension': OFF,
|
'react/jsx-filename-extension': OFF,
|
||||||
'react/jsx-one-expression-per-line': OFF,
|
'react/jsx-one-expression-per-line': OFF,
|
||||||
|
|
|
@ -129,13 +129,18 @@ class DocusaurusPluginContentBlog {
|
||||||
path: permalink,
|
path: permalink,
|
||||||
component: blogPageComponent,
|
component: blogPageComponent,
|
||||||
metadata: metadataItem,
|
metadata: metadataItem,
|
||||||
modules: metadataItem.posts.map(post => ({
|
modules: {
|
||||||
path: post.source,
|
entries: metadataItem.posts.map(post => ({
|
||||||
query: {
|
// To tell routes.js this is an import and not a nested object to recurse.
|
||||||
truncated: true,
|
__import: true,
|
||||||
},
|
path: post.source,
|
||||||
})),
|
query: {
|
||||||
|
truncated: true,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +148,9 @@ class DocusaurusPluginContentBlog {
|
||||||
path: permalink,
|
path: permalink,
|
||||||
component: blogPostComponent,
|
component: blogPostComponent,
|
||||||
metadata: metadataItem,
|
metadata: metadataItem,
|
||||||
modules: [metadataItem.source],
|
modules: {
|
||||||
|
content: metadataItem.source,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,9 @@ class DocusaurusPluginContentDocs {
|
||||||
path: metadataItem.permalink,
|
path: metadataItem.permalink,
|
||||||
component: docItemComponent,
|
component: docItemComponent,
|
||||||
metadata: metadataItem,
|
metadata: metadataItem,
|
||||||
modules: [metadataItem.source],
|
modules: {
|
||||||
|
content: metadataItem.source,
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,9 @@ class DocusaurusPluginContentPages {
|
||||||
path: permalink,
|
path: permalink,
|
||||||
component,
|
component,
|
||||||
metadata: metadataItem,
|
metadata: metadataItem,
|
||||||
modules: [source],
|
modules: {
|
||||||
|
content: source,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ describe('load utils', () => {
|
||||||
|
|
||||||
test('genComponentName', () => {
|
test('genComponentName', () => {
|
||||||
const asserts = {
|
const asserts = {
|
||||||
'/': 'Index',
|
'/': 'index',
|
||||||
'/foo-bar': 'FooBar096',
|
'/foo-bar': 'FooBar096',
|
||||||
'/foo/bar': 'FooBar1Df',
|
'/foo/bar': 'FooBar1Df',
|
||||||
'/blog/2017/12/14/introducing-docusaurus':
|
'/blog/2017/12/14/introducing-docusaurus':
|
||||||
|
@ -51,7 +51,7 @@ describe('load utils', () => {
|
||||||
test('docuHash', () => {
|
test('docuHash', () => {
|
||||||
const asserts = {
|
const asserts = {
|
||||||
'': '-d41',
|
'': '-d41',
|
||||||
'/': 'Index',
|
'/': 'index',
|
||||||
'/foo-bar': 'foo-bar-096',
|
'/foo-bar': 'foo-bar-096',
|
||||||
'/foo/bar': 'foo-bar-1df',
|
'/foo/bar': 'foo-bar-1df',
|
||||||
'/endi/lie': 'endi-lie-9fa',
|
'/endi/lie': 'endi-lie-9fa',
|
||||||
|
@ -81,7 +81,7 @@ describe('load utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('genChunkName', () => {
|
test('genChunkName', () => {
|
||||||
const asserts = {
|
let asserts = {
|
||||||
'/docs/adding-blog': 'docs-adding-blog-062',
|
'/docs/adding-blog': 'docs-adding-blog-062',
|
||||||
'/docs/versioning': 'docs-versioning-8a8',
|
'/docs/versioning': 'docs-versioning-8a8',
|
||||||
'/': 'index',
|
'/': 'index',
|
||||||
|
@ -94,6 +94,20 @@ describe('load utils', () => {
|
||||||
Object.keys(asserts).forEach(str => {
|
Object.keys(asserts).forEach(str => {
|
||||||
expect(genChunkName(str)).toBe(asserts[str]);
|
expect(genChunkName(str)).toBe(asserts[str]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Don't allow different chunk name for same path.
|
||||||
|
expect(genChunkName('path/is/similar', 'oldPrefix')).toEqual(
|
||||||
|
genChunkName('path/is/similar', 'newPrefix'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Even with same preferred name, still different chunk name for different path
|
||||||
|
asserts = {
|
||||||
|
'/blog/1': 'blog-85-f-089',
|
||||||
|
'/blog/2': 'blog-353-489',
|
||||||
|
};
|
||||||
|
Object.keys(asserts).forEach(str => {
|
||||||
|
expect(genChunkName(str, undefined, 'blog')).toBe(asserts[str]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('idx', () => {
|
test('idx', () => {
|
||||||
|
|
|
@ -47,7 +47,7 @@ function encodePath(userpath) {
|
||||||
*/
|
*/
|
||||||
function docuHash(str) {
|
function docuHash(str) {
|
||||||
if (str === '/') {
|
if (str === '/') {
|
||||||
return 'Index';
|
return 'index';
|
||||||
}
|
}
|
||||||
const shortHash = createHash('md5')
|
const shortHash = createHash('md5')
|
||||||
.update(str)
|
.update(str)
|
||||||
|
@ -63,7 +63,7 @@ function docuHash(str) {
|
||||||
*/
|
*/
|
||||||
function genComponentName(pagePath) {
|
function genComponentName(pagePath) {
|
||||||
if (pagePath === '/') {
|
if (pagePath === '/') {
|
||||||
return 'Index';
|
return 'index';
|
||||||
}
|
}
|
||||||
const pageHash = docuHash(pagePath);
|
const pageHash = docuHash(pagePath);
|
||||||
const pascalCase = _.flow(
|
const pascalCase = _.flow(
|
||||||
|
@ -88,9 +88,23 @@ function posixPath(str) {
|
||||||
return str.replace(/\\/g, '/');
|
return str.replace(/\\/g, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function genChunkName(str, prefix) {
|
const chunkNameCache = new Map();
|
||||||
const name = str === '/' ? 'index' : docuHash(str);
|
function genChunkName(modulePath, prefix, preferredName) {
|
||||||
return prefix ? `${prefix}---${name}` : name;
|
let chunkName = chunkNameCache.get(modulePath);
|
||||||
|
if (!chunkName) {
|
||||||
|
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;
|
||||||
|
chunkNameCache.set(modulePath, chunkName);
|
||||||
|
}
|
||||||
|
return chunkName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function idx(target, keyPaths) {
|
function idx(target, keyPaths) {
|
||||||
|
|
84
packages/docusaurus/lib/client/exports/ComponentCreator.js
Normal file
84
packages/docusaurus/lib/client/exports/ComponentCreator.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* 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 React from 'react';
|
||||||
|
import Loadable from 'react-loadable';
|
||||||
|
import Loading from '@theme/Loading';
|
||||||
|
import routesAsyncModules from '@generated/routesAsyncModules';
|
||||||
|
import registry from '@generated/registry';
|
||||||
|
|
||||||
|
function ComponentCreator(path) {
|
||||||
|
const modules = routesAsyncModules[path];
|
||||||
|
const originalModules = modules;
|
||||||
|
const optsModules = [];
|
||||||
|
const optsWebpack = [];
|
||||||
|
const mappedModules = {};
|
||||||
|
|
||||||
|
// Transform an object of
|
||||||
|
// {
|
||||||
|
// a: 'foo',
|
||||||
|
// b: { c: 'bar' },
|
||||||
|
// d: ['baz', 'qux']
|
||||||
|
// }
|
||||||
|
// into
|
||||||
|
// {
|
||||||
|
// a: () => import('foo'),
|
||||||
|
// b.c: () => import('bar'),
|
||||||
|
// d.0: () => import('baz'),
|
||||||
|
// d.1: () => import('qux'),
|
||||||
|
// }
|
||||||
|
// for React Loadable to process.
|
||||||
|
function traverseModules(module, keys) {
|
||||||
|
if (Array.isArray(module)) {
|
||||||
|
module.forEach((value, index) => {
|
||||||
|
traverseModules(value, [...keys, index]);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module === 'object') {
|
||||||
|
Object.keys(module).forEach(key => {
|
||||||
|
traverseModules(module[key], [...keys, key]);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mappedModules[keys.join('.')] = registry[module].importStatement;
|
||||||
|
optsModules.push(registry[module].module);
|
||||||
|
optsWebpack.push(registry[module].webpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseModules(modules, []);
|
||||||
|
|
||||||
|
return Loadable.Map({
|
||||||
|
loading: Loading,
|
||||||
|
loader: mappedModules,
|
||||||
|
// We need to provide opts.modules and opts.webpack to React Loadable
|
||||||
|
// Our loader is now dynamical, the react-loadable/babel won't do the heavy lifting for us.
|
||||||
|
// https://github.com/jamiebuilds/react-loadable#declaring-which-modules-are-being-loaded
|
||||||
|
modules: optsModules,
|
||||||
|
webpack: () => optsWebpack,
|
||||||
|
render: (loaded, props) => {
|
||||||
|
// Transform back loaded modules back into the original structure.
|
||||||
|
const loadedModules = originalModules;
|
||||||
|
Object.keys(loaded).forEach(key => {
|
||||||
|
let val = loadedModules;
|
||||||
|
const keyPath = key.split('.');
|
||||||
|
for (let i = 0; i < keyPath.length - 1; i += 1) {
|
||||||
|
val = val[keyPath[i]];
|
||||||
|
}
|
||||||
|
val[keyPath[keyPath.length - 1]] = loaded[key].default;
|
||||||
|
});
|
||||||
|
|
||||||
|
const Component = loadedModules.component;
|
||||||
|
delete loadedModules.component;
|
||||||
|
return <Component {...loadedModules} {...props} />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ComponentCreator;
|
|
@ -18,7 +18,7 @@ function BlogPage(props) {
|
||||||
const {baseUrl, favicon} = siteConfig;
|
const {baseUrl, favicon} = siteConfig;
|
||||||
const {
|
const {
|
||||||
metadata: {posts = []},
|
metadata: {posts = []},
|
||||||
modules: BlogPosts,
|
entries: BlogPosts,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -19,8 +19,8 @@ function BlogPost(props) {
|
||||||
);
|
);
|
||||||
const {baseUrl, favicon} = siteConfig;
|
const {baseUrl, favicon} = siteConfig;
|
||||||
const {language, title} = contextMetadata;
|
const {language, title} = contextMetadata;
|
||||||
const {modules, metadata} = props;
|
const {content, metadata} = props;
|
||||||
const BlogPostContents = modules[0];
|
const BlogPostContents = content;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|
|
@ -14,14 +14,14 @@ import Head from '@docusaurus/Head';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function DocBody(props) {
|
function DocBody(props) {
|
||||||
const {metadata, modules} = props;
|
const {metadata, content} = props;
|
||||||
const {language, version} = metadata;
|
const {language, version} = metadata;
|
||||||
const context = useContext(DocusaurusContext);
|
const context = useContext(DocusaurusContext);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
context.setContext({metadata});
|
context.setContext({metadata});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const DocContents = modules[0];
|
const DocContents = content;
|
||||||
return (
|
return (
|
||||||
<div className={styles.docBody}>
|
<div className={styles.docBody}>
|
||||||
<Head>
|
<Head>
|
||||||
|
|
|
@ -12,12 +12,12 @@ import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
|
|
||||||
import DocusaurusContext from '@docusaurus/context';
|
import DocusaurusContext from '@docusaurus/context';
|
||||||
|
|
||||||
function Pages({modules}) {
|
function Pages({content}) {
|
||||||
const context = useContext(DocusaurusContext);
|
const context = useContext(DocusaurusContext);
|
||||||
const {metadata = {}, siteConfig = {}} = context;
|
const {metadata = {}, siteConfig = {}} = context;
|
||||||
const {baseUrl, favicon} = siteConfig;
|
const {baseUrl, favicon} = siteConfig;
|
||||||
const {language} = metadata;
|
const {language} = metadata;
|
||||||
const PageContents = modules[0];
|
const PageContents = content;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|
|
@ -58,6 +58,7 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
|
|
||||||
// Routing
|
// Routing
|
||||||
const {
|
const {
|
||||||
|
registry,
|
||||||
routesAsyncModules,
|
routesAsyncModules,
|
||||||
routesConfig,
|
routesConfig,
|
||||||
routesMetadata,
|
routesMetadata,
|
||||||
|
@ -65,12 +66,19 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
routesPaths,
|
routesPaths,
|
||||||
} = await loadRoutes(pluginsRouteConfigs);
|
} = await loadRoutes(pluginsRouteConfigs);
|
||||||
|
|
||||||
// Mapping of routePath -> metadataPath. Example: '/blog' -> '@generated/metadata/blog-c06.json'
|
|
||||||
// Very useful to know which json metadata file is related to certain route
|
|
||||||
await generate(
|
await generate(
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
'routesMetadataPath.json',
|
'registry.js',
|
||||||
JSON.stringify(routesMetadataPath, null, 2),
|
`export default {
|
||||||
|
${Object.keys(registry)
|
||||||
|
.map(
|
||||||
|
key => ` '${key}': {
|
||||||
|
'importStatement': ${registry[key].importStatement},
|
||||||
|
'module': '${registry[key].modulePath}',
|
||||||
|
'webpack': require.resolveWeak('${registry[key].modulePath}'),
|
||||||
|
},`,
|
||||||
|
)
|
||||||
|
.join('\n')}};\n`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mapping of routePath -> async imported modules. Example: '/blog' -> ['@theme/BlogPage']
|
// Mapping of routePath -> async imported modules. Example: '/blog' -> ['@theme/BlogPage']
|
||||||
|
|
|
@ -63,6 +63,7 @@ module.exports = async function loadPlugins({pluginConfigs = [], context}) {
|
||||||
|
|
||||||
// 3. Plugin lifecycle - contentLoaded
|
// 3. Plugin lifecycle - contentLoaded
|
||||||
const pluginsRouteConfigs = [];
|
const pluginsRouteConfigs = [];
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
addRoute: config => pluginsRouteConfigs.push(config),
|
addRoute: config => pluginsRouteConfigs.push(config),
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,25 +7,27 @@
|
||||||
|
|
||||||
const {genChunkName, docuHash} = require('@docusaurus/utils');
|
const {genChunkName, docuHash} = require('@docusaurus/utils');
|
||||||
const {stringify} = require('querystring');
|
const {stringify} = require('querystring');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
async function loadRoutes(pluginsRouteConfigs) {
|
async function loadRoutes(pluginsRouteConfigs) {
|
||||||
const routesImports = [
|
const routesImports = [
|
||||||
`import React from 'react';`,
|
`import React from 'react';`,
|
||||||
`import Loadable from 'react-loadable';`,
|
|
||||||
`import Loading from '@theme/Loading';`,
|
|
||||||
`import NotFound from '@theme/NotFound';`,
|
`import NotFound from '@theme/NotFound';`,
|
||||||
|
`import ComponentCreator from '@docusaurus/ComponentCreator';`,
|
||||||
];
|
];
|
||||||
// Routes paths. Example: ['/', '/docs', '/blog/2017/09/03/test']
|
// Routes paths. Example: ['/', '/docs', '/blog/2017/09/03/test']
|
||||||
const routesPaths = [];
|
const routesPaths = [];
|
||||||
const addRoutesPath = routePath => {
|
const addRoutesPath = routePath => {
|
||||||
routesPaths.push(routePath);
|
routesPaths.push(routePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mapping of routePath -> metadataPath. Example: '/blog' -> '@generated/metadata/blog-c06.json'
|
// Mapping of routePath -> metadataPath. Example: '/blog' -> '@generated/metadata/blog-c06.json'
|
||||||
const routesMetadataPath = {};
|
const routesMetadataPath = {};
|
||||||
const addRoutesMetadataPath = routePath => {
|
const addRoutesMetadataPath = routePath => {
|
||||||
const fileName = `${docuHash(routePath)}.json`;
|
const fileName = `${docuHash(routePath)}.json`;
|
||||||
routesMetadataPath[routePath] = `@generated/metadata/${fileName}`;
|
routesMetadataPath[routePath] = `@generated/metadata/${fileName}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mapping of routePath -> metadata. Example: '/blog' -> { isBlogPage: true, permalink: '/blog' }
|
// Mapping of routePath -> metadata. Example: '/blog' -> { isBlogPage: true, permalink: '/blog' }
|
||||||
const routesMetadata = {};
|
const routesMetadata = {};
|
||||||
const addRoutesMetadata = (routePath, metadata) => {
|
const addRoutesMetadata = (routePath, metadata) => {
|
||||||
|
@ -33,13 +35,21 @@ async function loadRoutes(pluginsRouteConfigs) {
|
||||||
routesMetadata[routePath] = metadata;
|
routesMetadata[routePath] = metadata;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const registry = {};
|
||||||
|
|
||||||
// Mapping of routePath -> async imported modules. Example: '/blog' -> ['@theme/BlogPage']
|
// Mapping of routePath -> async imported modules. Example: '/blog' -> ['@theme/BlogPage']
|
||||||
const routesAsyncModules = {};
|
const routesAsyncModules = {};
|
||||||
const addRoutesAsyncModule = (routePath, module) => {
|
const addRoutesAsyncModule = (routePath, key, importChunk) => {
|
||||||
|
// TODO: Port other plugins to use modules and not rely on this.
|
||||||
if (!routesAsyncModules[routePath]) {
|
if (!routesAsyncModules[routePath]) {
|
||||||
routesAsyncModules[routePath] = [];
|
routesAsyncModules[routePath] = {};
|
||||||
}
|
}
|
||||||
routesAsyncModules[routePath].push(module);
|
routesAsyncModules[routePath][key] = importChunk.chunkName;
|
||||||
|
registry[importChunk.chunkName] = {
|
||||||
|
importStatement: importChunk.importStatement,
|
||||||
|
modulePath: importChunk.modulePath,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is the higher level overview of route code generation
|
// This is the higher level overview of route code generation
|
||||||
|
@ -48,7 +58,7 @@ async function loadRoutes(pluginsRouteConfigs) {
|
||||||
path: routePath,
|
path: routePath,
|
||||||
component,
|
component,
|
||||||
metadata,
|
metadata,
|
||||||
modules = [],
|
modules = {},
|
||||||
routes,
|
routes,
|
||||||
} = routeConfig;
|
} = routeConfig;
|
||||||
|
|
||||||
|
@ -58,9 +68,9 @@ async function loadRoutes(pluginsRouteConfigs) {
|
||||||
|
|
||||||
// Given an input (object or string), get the import path str
|
// Given an input (object or string), get the import path str
|
||||||
const getModulePath = target => {
|
const getModulePath = target => {
|
||||||
const isObj = typeof target === 'object';
|
const importStr = _.isObject(target) ? target.path : target;
|
||||||
const importStr = isObj ? target.path : target;
|
|
||||||
const queryStr = target.query ? `?${stringify(target.query)}` : '';
|
const queryStr = target.query ? `?${stringify(target.query)}` : '';
|
||||||
|
|
||||||
return `${importStr}${queryStr}`;
|
return `${importStr}${queryStr}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,71 +78,67 @@ async function loadRoutes(pluginsRouteConfigs) {
|
||||||
throw new Error(`path: ${routePath} need a component`);
|
throw new Error(`path: ${routePath} need a component`);
|
||||||
}
|
}
|
||||||
const componentPath = getModulePath(component);
|
const componentPath = getModulePath(component);
|
||||||
addRoutesAsyncModule(routePath, componentPath);
|
|
||||||
|
|
||||||
const genImportStr = (modulePath, prefix, name) => {
|
const genImportChunk = (modulePath, prefix, name) => {
|
||||||
const chunkName = genChunkName(name || modulePath, prefix);
|
const chunkName = genChunkName(modulePath, prefix, name);
|
||||||
const finalStr = JSON.stringify(modulePath);
|
const finalStr = JSON.stringify(modulePath);
|
||||||
return `() => import(/* webpackChunkName: '${chunkName}' */ ${finalStr})`;
|
return {
|
||||||
|
chunkName,
|
||||||
|
modulePath,
|
||||||
|
importStatement: `() => import(/* webpackChunkName: '${chunkName}' */ ${finalStr})`,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const componentChunk = genImportChunk(componentPath, 'component');
|
||||||
|
addRoutesAsyncModule(routePath, 'component', componentChunk);
|
||||||
|
|
||||||
if (routes) {
|
if (routes) {
|
||||||
const componentStr = `Loadable({
|
|
||||||
loader: ${genImportStr(componentPath, 'component')},
|
|
||||||
loading: Loading
|
|
||||||
})`;
|
|
||||||
return `
|
return `
|
||||||
{
|
{
|
||||||
path: '${routePath}',
|
path: '${routePath}',
|
||||||
component: ${componentStr},
|
component: ComponentCreator('${routePath}'),
|
||||||
routes: [${routes.map(generateRouteCode).join(',')}],
|
routes: [${routes.map(generateRouteCode).join(',')}],
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modulesImportStr = modules
|
function genRouteAsyncModule(value) {
|
||||||
.map((module, i) => {
|
if (Array.isArray(value)) {
|
||||||
const modulePath = getModulePath(module);
|
return value.map(genRouteAsyncModule);
|
||||||
addRoutesAsyncModule(routePath, modulePath);
|
}
|
||||||
return `Mod${i}: ${genImportStr(modulePath, i, routePath)},`;
|
|
||||||
})
|
|
||||||
.join('\n');
|
|
||||||
const modulesLoadedStr = modules
|
|
||||||
.map((module, i) => `loaded.Mod${i}.default,`)
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
let metadataImportStr = '';
|
if (_.isObject(value) && !value.__import) {
|
||||||
if (metadata) {
|
const newValue = {};
|
||||||
const metadataPath = routesMetadataPath[routePath];
|
Object.keys(value).forEach(key => {
|
||||||
addRoutesAsyncModule(routePath, metadataPath);
|
newValue[key] = genRouteAsyncModule(value[key]);
|
||||||
metadataImportStr = `metadata: ${genImportStr(
|
});
|
||||||
metadataPath,
|
return newValue;
|
||||||
'metadata',
|
}
|
||||||
|
|
||||||
|
const importChunk = genImportChunk(
|
||||||
|
getModulePath(value),
|
||||||
|
'module',
|
||||||
routePath,
|
routePath,
|
||||||
)},`;
|
);
|
||||||
|
registry[importChunk.chunkName] = {
|
||||||
|
importStatement: importChunk.importStatement,
|
||||||
|
modulePath: importChunk.modulePath,
|
||||||
|
};
|
||||||
|
return importChunk.chunkName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const componentStr = `Loadable.Map({
|
_.assign(routesAsyncModules[routePath], genRouteAsyncModule(modules));
|
||||||
loader: {
|
|
||||||
${modulesImportStr}
|
if (metadata) {
|
||||||
${metadataImportStr}
|
const metadataPath = routesMetadataPath[routePath];
|
||||||
Component: ${genImportStr(componentPath, 'component')},
|
const metadataChunk = genImportChunk(metadataPath, 'metadata', routePath);
|
||||||
},
|
addRoutesAsyncModule(routePath, 'metadata', metadataChunk);
|
||||||
loading: Loading,
|
}
|
||||||
render(loaded, props) {
|
|
||||||
const Component = loaded.Component.default;
|
|
||||||
const metadata = loaded.metadata || {};
|
|
||||||
const modules = [${modulesLoadedStr}];
|
|
||||||
return (
|
|
||||||
<Component {...props} metadata={metadata} modules={modules}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})\n`;
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
{
|
{
|
||||||
path: '${routePath}',
|
path: '${routePath}',
|
||||||
exact: true,
|
exact: true,
|
||||||
component: ${componentStr}
|
component: ComponentCreator('${routePath}')
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +158,7 @@ export default [
|
||||||
];\n`;
|
];\n`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
registry,
|
||||||
routesAsyncModules,
|
routesAsyncModules,
|
||||||
routesConfig,
|
routesConfig,
|
||||||
routesMetadata,
|
routesMetadata,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue