Skip to content

Commit

Permalink
ensure exports using singleton always reference the global
Browse files Browse the repository at this point in the history
  • Loading branch information
ndelangen committed Dec 13, 2022
1 parent aa747ea commit 775f3ae
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 29 deletions.
2 changes: 1 addition & 1 deletion code/lib/builder-vite/src/codegen-iframe-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export async function generateIframeScriptCode(options: ExtendedOptions) {
import { configure } from '${rendererName}';
import { logger } from '@storybook/client-logger';
import * as clientApi from "@storybook/preview-api";
import * as previewApi from "@storybook/preview-api";
${filesToImport(configEntries, 'config')}
import * as preview from '${virtualPreviewFile}';
Expand Down
46 changes: 18 additions & 28 deletions code/lib/preview-api/src/modules/client-api/ClientApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference types="webpack-env" />
/* eslint-disable no-underscore-dangle */

import { dedent } from 'ts-dedent';
import global from 'global';
Expand Down Expand Up @@ -29,10 +29,6 @@ import { combineParameters, composeStepRunners, normalizeInputTypes } from '../.

import { StoryStoreFacade } from './StoryStoreFacade';

// ClientApi (and StoreStore) are really singletons. However they are not created until the
// relevant framework instanciates them via `start.js`. The good news is this happens right away.
let singleton: ClientApi<Renderer>;

const warningAlternatives = {
addDecorator: `Instead, use \`export const decorators = [];\` in your \`preview.js\`.`,
addParameters: `Instead, use \`export const parameters = {};\` in your \`preview.js\`.`,
Expand All @@ -55,61 +51,61 @@ const checkMethod = (method: keyof typeof warningAlternatives) => {
);
}

if (!singleton) {
if (!global.__STORYBOOK_CLIENT_API__) {
throw new Error(`Singleton client API not yet initialized, cannot call \`${method}\`.`);
}
};

export const addDecorator = (decorator: DecoratorFunction<Renderer>) => {
checkMethod('addDecorator');
singleton.addDecorator(decorator);
global.__STORYBOOK_CLIENT_API__?.addDecorator(decorator);
};

export const addParameters = (parameters: Parameters) => {
checkMethod('addParameters');
singleton.addParameters(parameters);
global.__STORYBOOK_CLIENT_API__?.addParameters(parameters);
};

export const addLoader = (loader: LoaderFunction<Renderer>) => {
checkMethod('addLoader');
singleton.addLoader(loader);
global.__STORYBOOK_CLIENT_API__?.addLoader(loader);
};

export const addArgs = (args: Args) => {
checkMethod('addArgs');
singleton.addArgs(args);
global.__STORYBOOK_CLIENT_API__?.addArgs(args);
};

export const addArgTypes = (argTypes: ArgTypes) => {
checkMethod('addArgTypes');
singleton.addArgTypes(argTypes);
global.__STORYBOOK_CLIENT_API__?.addArgTypes(argTypes);
};

export const addArgsEnhancer = (enhancer: ArgsEnhancer<Renderer>) => {
checkMethod('addArgsEnhancer');
singleton.addArgsEnhancer(enhancer);
global.__STORYBOOK_CLIENT_API__?.addArgsEnhancer(enhancer);
};

export const addArgTypesEnhancer = (enhancer: ArgTypesEnhancer<Renderer>) => {
checkMethod('addArgTypesEnhancer');
singleton.addArgTypesEnhancer(enhancer);
global.__STORYBOOK_CLIENT_API__?.addArgTypesEnhancer(enhancer);
};

export const addStepRunner = (stepRunner: StepRunner) => {
checkMethod('addStepRunner');
singleton.addStepRunner(stepRunner);
global.__STORYBOOK_CLIENT_API__?.addStepRunner(stepRunner);
};

export const getGlobalRender = () => {
checkMethod('getGlobalRender');
return singleton.facade.projectAnnotations.render;
return global.__STORYBOOK_CLIENT_API__?.facade.projectAnnotations.render;
};

export const setGlobalRender = (
render: typeof singleton['facade']['projectAnnotations']['render']
) => {
export const setGlobalRender = (render: StoryStoreFacade<any>['projectAnnotations']['render']) => {
checkMethod('setGlobalRender');
singleton.facade.projectAnnotations.render = render;
if (global.__STORYBOOK_CLIENT_API__) {
global.__STORYBOOK_CLIENT_API__.facade.projectAnnotations.render = render;
}
};

const invalidStoryTypes = new Set(['string', 'number', 'boolean', 'symbol']);
Expand All @@ -132,8 +128,6 @@ export class ClientApi<TRenderer extends Renderer> {
this.addons = {};

this.storyStore = storyStore;

singleton = this as any;
}

importFn(path: Path) {
Expand Down Expand Up @@ -213,7 +207,6 @@ export class ClientApi<TRenderer extends Renderer> {
_addedExports = {} as Record<Path, ModuleExports>;

_loadAddedExports() {
// eslint-disable-next-line no-underscore-dangle
Object.entries(this._addedExports).forEach(([fileName, fileExports]) =>
this.facade.addStoriesFromExports(fileName, fileExports)
);
Expand Down Expand Up @@ -247,7 +240,7 @@ export class ClientApi<TRenderer extends Renderer> {
let i = 1;
// Deal with `storiesOf()` being called twice in the same file.
// On HMR, we clear _addedExports[fileName] below.
// eslint-disable-next-line no-underscore-dangle

while (this._addedExports[fileName]) {
i += 1;
fileName = `${baseFilename}-${i}`;
Expand All @@ -259,7 +252,7 @@ export class ClientApi<TRenderer extends Renderer> {
m.hot.accept();
m.hot.dispose(() => {
this.facade.clearFilenameExports(fileName);
// eslint-disable-next-line no-underscore-dangle

delete this._addedExports[fileName];

// We need to update the importFn as soon as the module re-evaluates
Expand All @@ -268,7 +261,6 @@ export class ClientApi<TRenderer extends Renderer> {
// debounce it somehow for initial startup. Instead, we'll take advantage of
// the fact that the evaluation of the module happens immediately in the same tick
setTimeout(() => {
// eslint-disable-next-line no-underscore-dangle
this._loadAddedExports();
this.onImportFnChanged?.({ importFn: this.importFn.bind(this) });
}, 0);
Expand Down Expand Up @@ -301,7 +293,7 @@ export class ClientApi<TRenderer extends Renderer> {
parameters: {},
};
// We map these back to a simple default export, even though we have type guarantees at this point
// eslint-disable-next-line no-underscore-dangle

this._addedExports[fileName] = { default: meta };

let counter = 0;
Expand All @@ -320,10 +312,8 @@ export class ClientApi<TRenderer extends Renderer> {

const { decorators, loaders, component, args, argTypes, ...storyParameters } = parameters;

// eslint-disable-next-line no-underscore-dangle
const storyId = parameters.__id || toId(kind, storyName);

// eslint-disable-next-line no-underscore-dangle
const csfExports = this._addedExports[fileName];
// Whack a _ on the front incase it is "default"
csfExports[`story${counter}`] = {
Expand Down

0 comments on commit 775f3ae

Please sign in to comment.