perf improvements

This commit is contained in:
Norbert de Langen 2022-11-15 13:12:12 +01:00
parent b105b21001
commit 1059a820bd
No known key found for this signature in database
GPG Key ID: FD0E78AF9A837762
7 changed files with 130 additions and 86 deletions

View File

@ -8,6 +8,8 @@
<link rel="shortcut icon" href="./favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="sb-preview/index.mjs" rel="preload" as="script">
<% if (typeof head !== 'undefined') { %> <%- head %> <% } %>
<style>

View File

@ -4,6 +4,10 @@
<meta charset="utf-8" />
<title><%= htmlWebpackPlugin.options.title || 'Storybook'%></title>
<% htmlWebpackPlugin.files.js.forEach(file => { %>
<link href="<%= file %>" rel="preload" as="script">
<% }); %>
<% if (htmlWebpackPlugin.files.favicon) { %>
<link rel="shortcut icon" href="<%= htmlWebpackPlugin.files.favicon%>" />
<% } %>

View File

@ -244,10 +244,6 @@ async function loadPresets(
return [];
}
if (!level) {
logger.info('=> Loading presets');
}
return (
await Promise.all(presets.map(async (preset) => loadPreset(preset, level, storybookOptions)))
).reduce((acc, loaded) => {

View File

@ -25,7 +25,7 @@ import { outputStats } from './utils/output-stats';
import { outputStartupInformation } from './utils/output-startup-information';
import { updateCheck } from './utils/update-check';
import { getServerPort, getServerChannelUrl } from './utils/server-address';
import { getBuilders } from './utils/get-builders';
import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders';
export async function buildDevStandalone(options: CLIOptions & LoadOptions & BuilderOptions) {
const { packageJson, versionUpdates, releaseNotes } = options;
@ -76,16 +76,23 @@ export async function buildDevStandalone(options: CLIOptions & LoadOptions & Bui
logger.warn(`you have not specified a framework in your ${options.configDir}/main.js`);
}
logger.info('=> Loading presets');
// Load first pass: We need to determine the builder
// We need to do this because builders might introduce 'overridePresets' which we need to take into account
// We hope to remove this in SB8
let presets = await loadAllPresets({
corePresets,
overridePresets: [],
...options,
});
const [previewBuilder, managerBuilder] = await getBuilders({ ...options, presets });
const { renderer } = await presets.apply<CoreConfig>('core', undefined);
const { renderer, builder } = await presets.apply<CoreConfig>('core', undefined);
const builderName = typeof builder === 'string' ? builder : builder?.name;
const [previewBuilder, managerBuilder] = await Promise.all([
getPreviewBuilder(builderName, options.configDir),
getManagerBuilder(),
]);
// Load second pass: all presets are applied in order
presets = await loadAllPresets({
corePresets: [
require.resolve('./presets/common-preset'),

View File

@ -18,10 +18,11 @@ import { getServer } from './utils/server-init';
import { useStatics } from './utils/server-statics';
import { useStoriesJson } from './utils/stories-json';
import { useStorybookMetadata } from './utils/metadata';
import type { ServerChannel } from './utils/get-server-channel';
import { getServerChannel } from './utils/get-server-channel';
import { openInBrowser } from './utils/open-in-browser';
import { getBuilders } from './utils/get-builders';
import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders';
import { StoryIndexGenerator } from './utils/StoryIndexGenerator';
import { summarizeIndex } from './utils/summarizeIndex';
@ -37,62 +38,24 @@ const versionStatus = (versionCheck: VersionCheck) => {
};
export async function storybookDevServer(options: Options) {
const startTime = process.hrtime();
const app = express();
const server = await getServer(app, options);
const [server, features, core] = await Promise.all([
getServer(app, options),
options.presets.apply<StorybookConfig['features']>('features'),
options.presets.apply<CoreConfig>('core'),
]);
const serverChannel = getServerChannel(server);
const features = await options.presets.apply<StorybookConfig['features']>('features');
const core = await options.presets.apply<CoreConfig>('core');
// try get index generator, if failed, send telemetry without storyCount, then rethrow the error
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
if (features?.buildStoriesJson || features?.storyStoreV7) {
const workingDir = process.cwd();
const directories = {
configDir: options.configDir,
workingDir,
};
const normalizedStories = normalizeStories(await options.presets.apply('stories'), directories);
const storyIndexers = await options.presets.apply('storyIndexers', []);
const docsOptions = await options.presets.apply<DocsOptions>('docs', {});
const initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = getStoryIndexGenerator(
features,
options,
serverChannel
);
const generator = new StoryIndexGenerator(normalizedStories, {
...directories,
storyIndexers,
docs: docsOptions,
workingDir,
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
storyStoreV7: features?.storyStoreV7,
});
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
useStoriesJson({
router,
initializedStoryIndexGenerator,
normalizedStories,
serverChannel,
workingDir,
});
}
if (!core?.disableTelemetry) {
initializedStoryIndexGenerator.then(async (generator) => {
const storyIndex = await generator?.getIndex();
const { versionCheck, versionUpdates } = options;
const payload = storyIndex
? {
versionStatus: versionUpdates ? versionStatus(versionCheck) : 'disabled',
storyIndex: summarizeIndex(storyIndex),
}
: undefined;
telemetry('dev', payload, { configDir: options.configDir });
});
}
if (!core?.disableProjectJson) {
useStorybookMetadata(router, options.configDir);
}
doTelemetry(core, initializedStoryIndexGenerator, options);
app.use(compression({ level: 1 }));
@ -119,8 +82,7 @@ export async function storybookDevServer(options: Options) {
}
// User's own static files
await useStatics(router, options);
const usingStatics = useStatics(router, options);
getMiddleware(options.configDir)(router);
app.use(router);
@ -129,44 +91,117 @@ export async function storybookDevServer(options: Options) {
const proto = options.https ? 'https' : 'http';
const { address, networkAddress } = getServerAddresses(port, host, proto);
await new Promise<void>((resolve, reject) => {
// FIXME: Following line doesn't match TypeScript signature at all 🤔
// @ts-expect-error (Converted from ts-ignore)
const listening = new Promise<void>((resolve, reject) => {
// @ts-expect-error (Following line doesn't match TypeScript signature at all 🤔)
server.listen({ port, host }, (error: Error) => (error ? reject(error) : resolve()));
});
const [previewBuilder, managerBuilder] = await getBuilders(options);
const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name;
const [previewBuilder, managerBuilder] = await Promise.all([
getPreviewBuilder(builderName, options.configDir),
getManagerBuilder(),
]);
if (options.debugWebpack) {
logConfig('Preview webpack config', await previewBuilder.getConfig(options));
}
Promise.all([initializedStoryIndexGenerator, listening, usingStatics]).then(async () => {
if (!options.ci && !options.smokeTest && options.open) {
openInBrowser(host ? networkAddress : address);
}
});
const managerResult = await managerBuilder.start({
startTime,
startTime: process.hrtime(),
options,
router,
server,
});
let previewResult;
if (!options.ignorePreview) {
try {
previewResult = await previewBuilder.start({
startTime,
options,
router,
server,
});
} catch (error) {
await managerBuilder?.bail();
throw error;
}
}
// TODO #13083 Move this to before starting the previewBuilder - when compiling the preview is so fast that it will be done before the browser is done opening
if (!options.ci && !options.smokeTest && options.open) {
openInBrowser(host ? networkAddress : address);
if (!options.ignorePreview) {
previewResult = await previewBuilder.start({
startTime: process.hrtime(),
options,
router,
server,
});
}
return { previewResult, managerResult, address, networkAddress };
}
async function doTelemetry(
core: CoreConfig,
initializedStoryIndexGenerator: Promise<StoryIndexGenerator>,
options: Options
) {
if (!core?.disableTelemetry) {
initializedStoryIndexGenerator.then(async (generator) => {
const storyIndex = await generator?.getIndex();
const { versionCheck, versionUpdates } = options;
const payload = storyIndex
? {
versionStatus: versionUpdates ? versionStatus(versionCheck) : 'disabled',
storyIndex: summarizeIndex(storyIndex),
}
: undefined;
telemetry('dev', payload, { configDir: options.configDir });
});
}
if (!core?.disableProjectJson) {
useStorybookMetadata(router, options.configDir);
}
}
async function getStoryIndexGenerator(
features: {
postcss?: boolean;
buildStoriesJson?: boolean;
previewCsfV3?: boolean;
storyStoreV7?: boolean;
breakingChangesV7?: boolean;
interactionsDebugger?: boolean;
babelModeV7?: boolean;
argTypeTargetsV7?: boolean;
warnOnLegacyHierarchySeparator?: boolean;
},
options: Options,
serverChannel: ServerChannel
) {
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
if (features?.buildStoriesJson || features?.storyStoreV7) {
const workingDir = process.cwd();
const directories = {
configDir: options.configDir,
workingDir,
};
const stories = options.presets.apply('stories');
const storyIndexers = options.presets.apply('storyIndexers', []);
const docsOptions = options.presets.apply<DocsOptions>('docs', {});
const normalizedStories = normalizeStories(await stories, directories);
const generator = new StoryIndexGenerator(normalizedStories, {
...directories,
storyIndexers: await storyIndexers,
docs: await docsOptions,
workingDir,
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
storyStoreV7: features?.storyStoreV7,
});
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
useStoriesJson({
router,
initializedStoryIndexGenerator,
normalizedStories,
serverChannel,
workingDir,
});
}
return initializedStoryIndexGenerator;
}

View File

@ -1,10 +1,10 @@
import type { Options, CoreConfig, Builder } from '@storybook/types';
async function getManagerBuilder() {
export async function getManagerBuilder() {
return import('@storybook/builder-manager');
}
async function getPreviewBuilder(builderName: string, configDir: string) {
export async function getPreviewBuilder(builderName: string, configDir: string) {
let builderPackage: string;
if (builderName) {
builderPackage = require.resolve(

View File

@ -7,7 +7,7 @@ import { dedent } from 'ts-dedent';
export function openInBrowser(address: string) {
getDefaultBrowser(async (err: any, res: any) => {
try {
if (res && (res.isChrome || res.isChromium)) {
if (res && (res.isChrome || res.isChromium || res.identity === 'com.brave.browser')) {
// We use betterOpn for Chrome because it is better at handling which chrome tab
// or window the preview loads in.
betterOpn(address);