mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-31 05:03:21 +08:00
WIP Got Story Rendering working
This commit is contained in:
parent
feca848f1f
commit
f7690afb17
56
addons/docs/src/blocks/DocsRenderer.tsx
Normal file
56
addons/docs/src/blocks/DocsRenderer.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React, { ComponentType, ReactElement } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AnyFramework, Parameters } from '@storybook/csf';
|
||||
import { ModuleExports, Story } from '@storybook/store';
|
||||
import type { DocsRenderFunction } from '@storybook/preview-web';
|
||||
import { DocsContainer } from './DocsContainer';
|
||||
import { DocsPage } from './DocsPage';
|
||||
|
||||
import { DocsContext, DocsContextProps } from './DocsContext';
|
||||
import { NoDocs } from './NoDocs';
|
||||
|
||||
// FIXME -- make this: DocsRenderFunction<TFramework>
|
||||
export function renderDocs<TFramework extends AnyFramework>(
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
docsParameters: Parameters,
|
||||
element: HTMLElement,
|
||||
callback: () => void
|
||||
): void {
|
||||
renderDocsAsync(docsContext, docsParameters, element).then(callback);
|
||||
}
|
||||
|
||||
async function renderDocsAsync<TFramework extends AnyFramework>(
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
docsParameters: Parameters,
|
||||
element: HTMLElement
|
||||
) {
|
||||
console.log(docsParameters);
|
||||
|
||||
// FIXME -- use DocsContainer, make it work for modern
|
||||
const SimpleContainer = ({ children }: any) => (
|
||||
<DocsContext.Provider value={docsContext}>{children} </DocsContext.Provider>
|
||||
);
|
||||
|
||||
const Container: ComponentType<{ context: DocsContextProps<TFramework> }> =
|
||||
docsParameters.container ||
|
||||
(await docsParameters.getContainer?.()) ||
|
||||
(docsContext.legacy ? DocsContainer : SimpleContainer);
|
||||
|
||||
const Page: ComponentType = docsParameters.page || (await docsParameters.getPage?.()) || DocsPage;
|
||||
console.log(docsParameters.page, Page);
|
||||
|
||||
// Use `title` as a key so that we force a re-render every time we switch components
|
||||
const docsElement = (
|
||||
<Container key={docsContext.title} context={docsContext}>
|
||||
<Page />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
ReactDOM.render(docsElement, element, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
export function unmountDocs(element: HTMLElement) {
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
}
|
@ -16,7 +16,7 @@ function getFirstStoryId(docsContext: DocsContextProps): string {
|
||||
|
||||
function renderAnchor() {
|
||||
const context = useContext(DocsContext);
|
||||
if (!context) {
|
||||
if (!context.legacy) {
|
||||
return null;
|
||||
}
|
||||
const anchorId = getFirstStoryId(context) || context.id;
|
||||
|
@ -36,6 +36,7 @@ type StoryDefProps = {
|
||||
|
||||
type StoryRefProps = {
|
||||
id?: string;
|
||||
of?: any;
|
||||
};
|
||||
|
||||
type StoryImportProps = {
|
||||
@ -55,7 +56,12 @@ export const lookupStoryId = (
|
||||
);
|
||||
|
||||
export const getStoryId = (props: StoryProps, context: DocsContextProps): StoryId => {
|
||||
const { id } = props as StoryRefProps;
|
||||
const { id, of } = props as StoryRefProps;
|
||||
|
||||
if (of) {
|
||||
return context.storyIdByModuleExport(of);
|
||||
}
|
||||
|
||||
const { name } = props as StoryDefProps;
|
||||
const inputId = id === CURRENT_SELECTION ? context.id : id;
|
||||
return inputId || lookupStoryId(name, context);
|
||||
@ -127,6 +133,8 @@ const Story: FunctionComponent<StoryProps> = (props) => {
|
||||
const story = useStory(storyId, context);
|
||||
const [showLoader, setShowLoader] = useState(true);
|
||||
|
||||
console.log(storyId, story);
|
||||
|
||||
useEffect(() => {
|
||||
let cleanup: () => void;
|
||||
if (story && storyRef.current) {
|
||||
|
@ -16,7 +16,7 @@ export function useStories<TFramework extends AnyFramework = AnyFramework>(
|
||||
storyIds: StoryId[],
|
||||
context: DocsContextProps<TFramework>
|
||||
): (Story<TFramework> | void)[] {
|
||||
const initialStoriesById = context.componentStories().reduce((acc, story) => {
|
||||
const initialStoriesById = context.preloadedStories().reduce((acc, story) => {
|
||||
acc[story.id] = story;
|
||||
return acc;
|
||||
}, {} as Record<StoryId, Story<TFramework>>);
|
||||
|
@ -1,6 +1,9 @@
|
||||
export const parameters = {
|
||||
docs: {
|
||||
getContainer: async () => (await import('./blocks')).DocsContainer,
|
||||
getPage: async () => (await import('./blocks')).DocsPage,
|
||||
renderer: async () => {
|
||||
const x = await import('./blocks/DocsRenderer');
|
||||
console.log(x);
|
||||
return x;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Meta } from '@storybook/addon-docs';
|
||||
import meta from '../button.stories';
|
||||
import { Meta, Story } from '@storybook/addon-docs';
|
||||
import meta, { Basic } from '../button.stories';
|
||||
|
||||
<Meta of={meta} />
|
||||
|
||||
# Docs with of
|
||||
|
||||
hello docs
|
||||
|
||||
<Story of={Basic} />
|
||||
|
@ -57,10 +57,6 @@
|
||||
"unfetch": "^4.2.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
@ -5,7 +5,7 @@ import { Channel } from '@storybook/addons';
|
||||
import { DOCS_RENDERED } from '@storybook/core-events';
|
||||
|
||||
import { Render, RenderType } from './StoryRender';
|
||||
import type { DocsContextProps } from './types';
|
||||
import type { DocsContextProps, DocsRenderFunction } from './types';
|
||||
|
||||
export class DocsRender<TFramework extends AnyFramework> implements Render<TFramework> {
|
||||
public type: RenderType = 'docs';
|
||||
@ -22,10 +22,12 @@ export class DocsRender<TFramework extends AnyFramework> implements Render<TFram
|
||||
|
||||
private canvasElement?: HTMLElement;
|
||||
|
||||
private context?: DocsContextProps;
|
||||
private context?: DocsContextProps<TFramework>;
|
||||
|
||||
public disableKeyListeners = false;
|
||||
|
||||
public teardown: (options: { viewModeChanged?: boolean }) => Promise<void>;
|
||||
|
||||
constructor(
|
||||
private channel: Channel,
|
||||
private store: StoryStore<TFramework>,
|
||||
@ -56,22 +58,17 @@ export class DocsRender<TFramework extends AnyFramework> implements Render<TFram
|
||||
return this.preparing;
|
||||
}
|
||||
|
||||
async renderToElement(
|
||||
canvasElement: HTMLElement,
|
||||
renderStoryToElement: DocsContextProps['renderStoryToElement']
|
||||
) {
|
||||
this.canvasElement = canvasElement;
|
||||
|
||||
async docsContext(
|
||||
renderStoryToElement: DocsContextProps<TFramework>['renderStoryToElement']
|
||||
): Promise<DocsContextProps<TFramework>> {
|
||||
const { id, title, name } = this.entry;
|
||||
const csfFile: CSFFile<TFramework> = await this.store.loadCSFFileByStoryId(this.id);
|
||||
|
||||
this.context = {
|
||||
const base = {
|
||||
legacy: this.legacy,
|
||||
id,
|
||||
title,
|
||||
name,
|
||||
// NOTE: these two functions are *sync* so cannot access stories from other CSF files
|
||||
storyById: (storyId: StoryId) => this.store.storyFromCSFFile({ storyId, csfFile }),
|
||||
componentStories: () => this.store.componentStoriesFromCSFFile({ csfFile }),
|
||||
loadStory: (storyId: StoryId) => this.store.loadStory({ storyId }),
|
||||
renderStoryToElement,
|
||||
getStoryContext: (renderedStory: Story<TFramework>) =>
|
||||
@ -79,10 +76,45 @@ export class DocsRender<TFramework extends AnyFramework> implements Render<TFram
|
||||
...this.store.getStoryContext(renderedStory),
|
||||
viewMode: 'docs' as ViewMode,
|
||||
} as StoryContextForLoaders<TFramework>),
|
||||
// Put all the storyContext fields onto the docs context for back-compat
|
||||
...(!global.FEATURES?.breakingChangesV7 && this.store.getStoryContext(this.story)),
|
||||
};
|
||||
|
||||
if (this.legacy) {
|
||||
const componentStories = () => this.store.componentStoriesFromCSFFile({ csfFile });
|
||||
return {
|
||||
...base,
|
||||
|
||||
// NOTE: these two functions are *sync* so cannot access stories from other CSF files
|
||||
storyIdByModuleExport: () => {
|
||||
throw new Error('`storyIdByModuleExport` not available for legacy docs files.');
|
||||
},
|
||||
storyById: (storyId: StoryId) => this.store.storyFromCSFFile({ storyId, csfFile }),
|
||||
|
||||
componentStories,
|
||||
preloadedStories: componentStories,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...base,
|
||||
storyIdByModuleExport: (moduleExport) => this.store.storyIdByModuleExport({ moduleExport }),
|
||||
storyById: () => {
|
||||
throw new Error('`storyById` not available for modern docs files.');
|
||||
},
|
||||
|
||||
componentStories: () => {
|
||||
throw new Error('You cannot render all the stories for a component in a docs.mdx file');
|
||||
},
|
||||
preloadedStories: () => [], // FIXME
|
||||
};
|
||||
}
|
||||
|
||||
async renderToElement(
|
||||
canvasElement: HTMLElement,
|
||||
renderStoryToElement: DocsContextProps['renderStoryToElement']
|
||||
) {
|
||||
this.canvasElement = canvasElement;
|
||||
this.context = await this.docsContext(renderStoryToElement);
|
||||
|
||||
return this.render();
|
||||
}
|
||||
|
||||
@ -90,17 +122,29 @@ export class DocsRender<TFramework extends AnyFramework> implements Render<TFram
|
||||
if (!(this.story || this.exports) || !this.context || !this.canvasElement)
|
||||
throw new Error('DocsRender not ready to render');
|
||||
|
||||
const renderer = await import('./renderDocs');
|
||||
const { docs } = this.story?.parameters || this.store.projectAnnotations.parameters;
|
||||
|
||||
if (this.legacy) {
|
||||
renderer.renderLegacyDocs(this.story, this.context, this.canvasElement, () =>
|
||||
this.channel.emit(DOCS_RENDERED, this.id)
|
||||
);
|
||||
} else {
|
||||
renderer.renderDocs(this.exports, this.context, this.canvasElement, () =>
|
||||
this.channel.emit(DOCS_RENDERED, this.id)
|
||||
if (!docs) {
|
||||
throw new Error(
|
||||
`Cannot render a story in viewMode=docs if \`@storybook/addon-docs\` is not installed`
|
||||
);
|
||||
}
|
||||
|
||||
const renderer = await docs.renderer();
|
||||
(renderer.renderDocs as DocsRenderFunction<TFramework>)(
|
||||
this.context,
|
||||
{
|
||||
...docs,
|
||||
...(!this.legacy && { page: this.exports.default }),
|
||||
},
|
||||
this.canvasElement,
|
||||
() => this.channel.emit(DOCS_RENDERED, this.id)
|
||||
);
|
||||
this.teardown = async ({ viewModeChanged }: { viewModeChanged?: boolean } = {}) => {
|
||||
if (!viewModeChanged || !this.canvasElement) return;
|
||||
// TODO type
|
||||
renderer.unmountDocs(this.canvasElement);
|
||||
};
|
||||
}
|
||||
|
||||
async rerender() {
|
||||
@ -110,10 +154,4 @@ export class DocsRender<TFramework extends AnyFramework> implements Render<TFram
|
||||
// docs page when a single story changes.
|
||||
if (!global.FEATURES?.modernInlineRender) await this.render();
|
||||
}
|
||||
|
||||
async teardown({ viewModeChanged }: { viewModeChanged?: boolean } = {}) {
|
||||
if (!viewModeChanged || !this.canvasElement) return;
|
||||
const renderer = await import('./renderDocs');
|
||||
renderer.unmountDocs(this.canvasElement);
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +274,6 @@ export class PreviewWeb<TFramework extends AnyFramework> extends Preview<TFramew
|
||||
} else {
|
||||
render = new DocsRender<TFramework>(this.channel, this.storyStore, entry);
|
||||
}
|
||||
console.log(render);
|
||||
|
||||
// We need to store this right away, so if the story changes during
|
||||
// the async `.prepare()` below, we can (potentially) cancel it
|
||||
|
@ -1,81 +0,0 @@
|
||||
import React, { ComponentType, ReactElement } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AnyFramework } from '@storybook/csf';
|
||||
import { ModuleExports, Story } from '@storybook/store';
|
||||
|
||||
import { DocsContextProps } from './types';
|
||||
import { NoDocs } from './NoDocs';
|
||||
|
||||
export function renderLegacyDocs<TFramework extends AnyFramework>(
|
||||
story: Story<TFramework>,
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
element: HTMLElement,
|
||||
callback: () => void
|
||||
) {
|
||||
return renderLegacyDocsAsync(story, docsContext, element).then(callback);
|
||||
}
|
||||
|
||||
export function renderDocs<TFramework extends AnyFramework>(
|
||||
exports: ModuleExports,
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
element: HTMLElement,
|
||||
callback: () => void
|
||||
) {
|
||||
return renderDocsAsync(exports, docsContext, element).then(callback);
|
||||
}
|
||||
|
||||
async function renderLegacyDocsAsync<TFramework extends AnyFramework>(
|
||||
story: Story<TFramework>,
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
element: HTMLElement
|
||||
) {
|
||||
const { docs } = story.parameters;
|
||||
if ((docs?.getPage || docs?.page) && !(docs?.getContainer || docs?.container)) {
|
||||
throw new Error('No `docs.container` set, did you run `addon-docs/preset`?');
|
||||
}
|
||||
|
||||
const DocsContainer: ComponentType<{ context: DocsContextProps<TFramework> }> =
|
||||
docs.container ||
|
||||
(await docs.getContainer?.()) ||
|
||||
(({ children }: { children: Element }) => <>{children}</>);
|
||||
|
||||
const Page: ComponentType = docs.page || (await docs.getPage?.()) || NoDocs;
|
||||
|
||||
// Use `componentId` as a key so that we force a re-render every time
|
||||
// we switch components
|
||||
const docsElement = (
|
||||
<DocsContainer key={story.componentId} context={docsContext}>
|
||||
<Page />
|
||||
</DocsContainer>
|
||||
);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
ReactDOM.render(docsElement, element, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
async function renderDocsAsync<TFramework extends AnyFramework>(
|
||||
exports: ModuleExports,
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
element: HTMLElement
|
||||
) {
|
||||
// FIXME -- is this at all correct?
|
||||
const DocsContainer = ({ children }: { children: ReactElement }) => <>{children}</>;
|
||||
|
||||
const Page = exports.default;
|
||||
|
||||
// FIXME -- do we need to set a key as above?
|
||||
const docsElement = (
|
||||
<DocsContainer>
|
||||
<Page />
|
||||
</DocsContainer>
|
||||
);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
ReactDOM.render(docsElement, element, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
export function unmountDocs(element: HTMLElement) {
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
}
|
@ -4,21 +4,27 @@ import type {
|
||||
AnyFramework,
|
||||
StoryContextForLoaders,
|
||||
ComponentTitle,
|
||||
Args,
|
||||
Globals,
|
||||
Parameters,
|
||||
} from '@storybook/csf';
|
||||
import type { Story } from '@storybook/store';
|
||||
import { PreviewWeb } from './PreviewWeb';
|
||||
|
||||
export interface DocsContextProps<TFramework extends AnyFramework = AnyFramework> {
|
||||
legacy: boolean;
|
||||
|
||||
id: StoryId;
|
||||
title: ComponentTitle;
|
||||
name: StoryName;
|
||||
|
||||
storyIdByModuleExport: (moduleExport: any) => StoryId;
|
||||
storyById: (id: StoryId) => Story<TFramework>;
|
||||
getStoryContext: (story: Story<TFramework>) => StoryContextForLoaders<TFramework>;
|
||||
|
||||
componentStories: () => Story<TFramework>[];
|
||||
preloadedStories: () => Story<TFramework>[];
|
||||
|
||||
loadStory: (id: StoryId) => Promise<Story<TFramework>>;
|
||||
renderStoryToElement: PreviewWeb<TFramework>['renderStoryToElement'];
|
||||
getStoryContext: (story: Story<TFramework>) => StoryContextForLoaders<TFramework>;
|
||||
|
||||
/**
|
||||
* mdxStoryNameToKey is an MDX-compiler-generated mapping of an MDX story's
|
||||
@ -27,16 +33,11 @@ export interface DocsContextProps<TFramework extends AnyFramework = AnyFramework
|
||||
*/
|
||||
mdxStoryNameToKey?: Record<string, string>;
|
||||
mdxComponentAnnotations?: any;
|
||||
|
||||
// These keys are deprecated and will be removed in v7
|
||||
/** @deprecated */
|
||||
kind?: ComponentTitle;
|
||||
/** @deprecated */
|
||||
story?: StoryName;
|
||||
/** @deprecated */
|
||||
args?: Args;
|
||||
/** @deprecated */
|
||||
globals?: Globals;
|
||||
/** @deprecated */
|
||||
parameters?: Globals;
|
||||
}
|
||||
|
||||
export type DocsRenderFunction<TFramework extends AnyFramework> = (
|
||||
docsContext: DocsContextProps<TFramework>,
|
||||
docsParameters: Parameters,
|
||||
element: HTMLElement,
|
||||
callback: () => void
|
||||
) => void;
|
||||
|
@ -62,6 +62,11 @@ export class StoryStore<TFramework extends AnyFramework> {
|
||||
|
||||
resolveInitializationPromise: () => void;
|
||||
|
||||
/**
|
||||
* A map of module export to story id, for later consumption
|
||||
*/
|
||||
moduleExportMap: Map<any, StoryId> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.globals = new GlobalsStore();
|
||||
this.args = new ArgsStore();
|
||||
@ -144,7 +149,7 @@ export class StoryStore<TFramework extends AnyFramework> {
|
||||
const { importPath, title } = this.storyIndex.storyIdToEntry(storyId);
|
||||
return this.importFn(importPath).then((moduleExports) =>
|
||||
// We pass the title in here as it may have been generated by autoTitle on the server.
|
||||
this.processCSFFileWithCache(moduleExports, importPath, title)
|
||||
this.processCSFFileWithCache(moduleExports, importPath, title, this.moduleExportMap)
|
||||
);
|
||||
}
|
||||
|
||||
@ -184,6 +189,12 @@ export class StoryStore<TFramework extends AnyFramework> {
|
||||
return this.storyFromCSFFile({ storyId, csfFile });
|
||||
}
|
||||
|
||||
storyIdByModuleExport({ moduleExport }: { moduleExport: any }) {
|
||||
if (this.moduleExportMap.has(moduleExport)) return this.moduleExportMap.get(moduleExport);
|
||||
|
||||
throw new Error(`Couldn't find story for that export: ${moduleExport}.`);
|
||||
}
|
||||
|
||||
// This function is synchronous for convenience -- often times if you have a CSF file already
|
||||
// it is easier not to have to await `loadStory`.
|
||||
storyFromCSFFile({
|
||||
|
@ -1,10 +1,16 @@
|
||||
import type { Parameters, AnyFramework, ComponentTitle } from '@storybook/csf';
|
||||
import type { Parameters, AnyFramework, ComponentTitle, StoryId } from '@storybook/csf';
|
||||
import { isExportStory } from '@storybook/csf';
|
||||
import { logger } from '@storybook/client-logger';
|
||||
|
||||
import { normalizeStory } from './normalizeStory';
|
||||
import { normalizeComponentAnnotations } from './normalizeComponentAnnotations';
|
||||
import type { ModuleExports, CSFFile, NormalizedComponentAnnotations, Path } from '../types';
|
||||
import type {
|
||||
ModuleExports,
|
||||
CSFFile,
|
||||
NormalizedComponentAnnotations,
|
||||
Path,
|
||||
NormalizedStoryAnnotations,
|
||||
} from '../types';
|
||||
|
||||
const checkGlobals = (parameters: Parameters) => {
|
||||
const { globals, globalTypes } = parameters;
|
||||
@ -36,7 +42,8 @@ const checkDisallowedParameters = (parameters: Parameters) => {
|
||||
export function processCSFFile<TFramework extends AnyFramework>(
|
||||
moduleExports: ModuleExports,
|
||||
importPath: Path,
|
||||
title: ComponentTitle
|
||||
title: ComponentTitle,
|
||||
moduleExportMap?: Map<any, StoryId>
|
||||
): CSFFile<TFramework> {
|
||||
const { default: defaultExport, __namedExportsOrder, ...namedExports } = moduleExports;
|
||||
|
||||
@ -51,6 +58,8 @@ export function processCSFFile<TFramework extends AnyFramework>(
|
||||
const storyMeta = normalizeStory(key, namedExports[key], meta);
|
||||
checkDisallowedParameters(storyMeta.parameters);
|
||||
|
||||
// Track which exports get turned into which ids
|
||||
if (moduleExportMap) moduleExportMap.set(namedExports[key], storyMeta.id);
|
||||
csfFile.stories[storyMeta.id] = storyMeta;
|
||||
}
|
||||
});
|
||||
|
12
yarn.lock
12
yarn.lock
@ -8631,14 +8631,11 @@ __metadata:
|
||||
ts-dedent: ^2.0.0
|
||||
unfetch: ^4.2.0
|
||||
util-deprecate: ^1.0.2
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@storybook/preview-web@npm:6.5.0-beta.1":
|
||||
version: 6.5.0-beta.1
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@storybook/preview-web@npm:6.5.0-beta.1"
|
||||
dependencies:
|
||||
"@storybook/addons": 6.5.0-beta.1
|
||||
@ -8657,12 +8654,9 @@ __metadata:
|
||||
ts-dedent: ^2.0.0
|
||||
unfetch: ^4.2.0
|
||||
util-deprecate: ^1.0.2
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 0ea1e2d1b295b1f07c462b497dc878ac4f61c5eb810f16a0b6e923869f7b4e591af783df7edacd555178f1b4a41eebe789c3e259973299a81f0dda699c6e3581
|
||||
languageName: node
|
||||
linkType: hard
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@storybook/react-docgen-typescript-plugin@npm:1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0":
|
||||
version: 1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user