refactor(v2): avoid synchronous/ blocking operation when possible (#1957)

* perf(v2): avoid synchronous/ blocking operation when possible

* save variable
This commit is contained in:
Endi 2019-11-11 20:56:23 +07:00 committed by GitHub
parent 5e445a0011
commit 1235fc9f7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 76 deletions

View file

@ -52,4 +52,10 @@ describe('loadSidebars', () => {
const result = loadSidebars(null); const result = loadSidebars(null);
expect(result).toEqual({}); expect(result).toEqual({});
}); });
test('fake sidebars path', () => {
expect(() => {
loadSidebars('/fake/path');
}).toThrowError();
});
}); });

View file

@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import fs from 'fs';
import importFresh from 'import-fresh'; import importFresh from 'import-fresh';
import { import {
SidebarItemCategory, SidebarItemCategory,
@ -105,7 +104,7 @@ function normalizeSidebar(sidebars: SidebarRaw): Sidebar {
export default function loadSidebars(sidebarPath: string): Sidebar { export default function loadSidebars(sidebarPath: string): Sidebar {
// We don't want sidebars to be cached because of hotreloading. // We don't want sidebars to be cached because of hotreloading.
let allSidebars: SidebarRaw = {}; let allSidebars: SidebarRaw = {};
if (sidebarPath && fs.existsSync(sidebarPath)) { if (sidebarPath) {
allSidebars = importFresh(sidebarPath) as SidebarRaw; allSidebars = importFresh(sidebarPath) as SidebarRaw;
} }
return normalizeSidebar(allSidebars); return normalizeSidebar(allSidebars);

View file

@ -14,75 +14,74 @@ import {getBundles} from 'react-loadable-ssr-addon';
import Loadable from 'react-loadable'; import Loadable from 'react-loadable';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs-extra';
import routes from '@generated/routes'; import routes from '@generated/routes';
import preload from './preload'; import preload from './preload';
import App from './App'; import App from './App';
import ssrTemplate from './templates/ssr.html.template'; import ssrTemplate from './templates/ssr.html.template';
// Renderer for static-site-generator-webpack-plugin (async rendering via promises) // Renderer for static-site-generator-webpack-plugin (async rendering via promises)
export default function render(locals) { export default async function render(locals) {
const {routesLocation} = locals; const {routesLocation} = locals;
const location = routesLocation[locals.path]; const location = routesLocation[locals.path];
return preload(routes, location).then(() => { await preload(routes, location);
const modules = new Set(); const modules = new Set();
const context = {}; const context = {};
const appHtml = ReactDOMServer.renderToString( const appHtml = ReactDOMServer.renderToString(
<Loadable.Capture report={moduleName => modules.add(moduleName)}> <Loadable.Capture report={moduleName => modules.add(moduleName)}>
<StaticRouter location={location} context={context}> <StaticRouter location={location} context={context}>
<App /> <App />
</StaticRouter> </StaticRouter>
</Loadable.Capture>, </Loadable.Capture>,
); );
const helmet = Helmet.renderStatic(); const helmet = Helmet.renderStatic();
const htmlAttributes = helmet.htmlAttributes.toString(); const htmlAttributes = helmet.htmlAttributes.toString();
const bodyAttributes = helmet.bodyAttributes.toString(); const bodyAttributes = helmet.bodyAttributes.toString();
const metaStrings = [ const metaStrings = [
helmet.title.toString(), helmet.title.toString(),
helmet.meta.toString(), helmet.meta.toString(),
helmet.link.toString(), helmet.link.toString(),
]; ];
const metaAttributes = metaStrings.filter(Boolean); const metaAttributes = metaStrings.filter(Boolean);
const {outDir} = locals; const {outDir} = locals;
const manifestPath = path.join(outDir, 'client-manifest.json'); const manifestPath = path.join(outDir, 'client-manifest.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
// chunkName -> chunkAssets mapping. // chunkName -> chunkAssets mapping.
const chunkManifestPath = path.join(outDir, 'chunk-map.json'); const chunkManifestPath = path.join(outDir, 'chunk-map.json');
const chunkManifest = JSON.parse( const chunkManifest = JSON.parse(
fs.readFileSync(chunkManifestPath, 'utf-8'), await fs.readFile(chunkManifestPath, 'utf8'),
); );
const chunkManifestScript = const chunkManifestScript =
`<script type="text/javascript">` + `<script type="text/javascript">` +
`/*<![CDATA[*/window.__chunkMapping=${JSON.stringify( `/*<![CDATA[*/window.__chunkMapping=${JSON.stringify(
chunkManifest, chunkManifest,
)};/*]]>*/` + )};/*]]>*/` +
`</script>`; `</script>`;
// Get all required assets for this particular page based on client manifest information // Get all required assets for this particular page based on client manifest information
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)]; const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
const bundles = getBundles(manifest, modulesToBeLoaded); const bundles = getBundles(manifest, modulesToBeLoaded);
const stylesheets = (bundles.css || []).map(b => b.file); const stylesheets = (bundles.css || []).map(b => b.file);
const scripts = (bundles.js || []).map(b => b.file); const scripts = (bundles.js || []).map(b => b.file);
const {baseUrl} = locals; const {baseUrl} = locals;
return ejs.render( return ejs.render(
ssrTemplate.trim(), ssrTemplate.trim(),
{ {
appHtml, appHtml,
baseUrl, baseUrl,
chunkManifestScript, chunkManifestScript,
htmlAttributes: htmlAttributes || '', htmlAttributes: htmlAttributes || '',
bodyAttributes: bodyAttributes || '', bodyAttributes: bodyAttributes || '',
metaAttributes, metaAttributes,
scripts, scripts,
stylesheets, stylesheets,
}, },
{ {
rmWhitespace: true, rmWhitespace: true,
}, },
); );
});
} }

View file

@ -110,7 +110,9 @@ export async function build(
// Remove server.bundle.js because it is useless // Remove server.bundle.js because it is useless
if (serverConfig.output && serverConfig.output.filename) { if (serverConfig.output && serverConfig.output.filename) {
const serverBundle = path.join(outDir, serverConfig.output.filename); const serverBundle = path.join(outDir, serverConfig.output.filename);
fs.existsSync(serverBundle) && fs.unlinkSync(serverBundle); fs.pathExists(serverBundle).then(exist => {
exist && fs.unlink(serverBundle);
});
} }
/* Plugin lifecycle - postBuild */ /* Plugin lifecycle - postBuild */

View file

@ -26,7 +26,7 @@ class ChunkManifestPlugin {
const {path: outputPath, publicPath} = compiler.options.output; const {path: outputPath, publicPath} = compiler.options.output;
// Build the chunk mapping // Build the chunk mapping
compiler.hooks.afterCompile.tap(pluginName, compilation => { compiler.hooks.afterCompile.tapAsync(pluginName, (compilation, done) => {
const assets = {}; const assets = {};
const assetsMap = {}; const assetsMap = {};
// eslint-disable-next-line // eslint-disable-next-line
@ -50,8 +50,11 @@ class ChunkManifestPlugin {
chunkManifest = assetsMap; chunkManifest = assetsMap;
if (!this.options.inlineManifest) { if (!this.options.inlineManifest) {
const finalPath = path.resolve(outputPath, this.options.filename); const finalPath = path.resolve(outputPath, this.options.filename);
fs.ensureDirSync(path.dirname(finalPath)); fs.ensureDir(path.dirname(finalPath), () => {
fs.writeFileSync(finalPath, JSON.stringify(chunkManifest, null, 2)); fs.writeFile(finalPath, JSON.stringify(chunkManifest, null, 2), done);
});
} else {
done();
} }
}); });

View file

@ -18,19 +18,19 @@ class WaitPlugin {
// Before finishing the compilation step // Before finishing the compilation step
compiler.hooks.make.tapAsync('WaitPlugin', (compilation, callback) => { compiler.hooks.make.tapAsync('WaitPlugin', (compilation, callback) => {
// To prevent 'waitFile' error on waiting non-existing directory // To prevent 'waitFile' error on waiting non-existing directory
fs.ensureDirSync(path.dirname(this.filepath)); fs.ensureDir(path.dirname(this.filepath), () => {
// Wait until file exist
// Wait until file exist waitFile({
waitFile({ resources: [this.filepath],
resources: [this.filepath], interval: 300,
interval: 300,
})
.then(() => {
callback();
}) })
.catch(error => { .then(() => {
console.warn(`WaitPlugin error: ${error}`); callback();
}); })
.catch(error => {
console.warn(`WaitPlugin error: ${error}`);
});
});
}); });
} }
} }