UNPKG

storybook-addon-source-link

Version:

Add a button to the Storybook toolbar that opens the file containing the Story in an IDE like VSCode.

1 lines 7.42 kB
{"version":3,"sources":["../src/preview.tsx","../src/linkUtil.ts","../src/preview/parameterResolver.ts","../src/constants.ts"],"names":["DocsContainer","join","normalize","parse","getFileUrl","rootPath","importPath","React","addons","useEffect","ADDON_ID","TOOL_ID","EVENTS","useParameterResolver","parameter","disabled","channel","handler","context","resolvedParameters","id","entry","resolvedEntry","resolveLinkEntry","withParameterResolver","StoryFn","ctx","ParameterResolver","value","params","parameters","componentPath","componentFileUrl","href","children","props","projectAnnotations","decorators"],"mappings":"AAAA,OAAS,iBAAAA,MAA8C,oBCAvD,OAAS,QAAAC,EAAM,aAAAC,EAAW,SAAAC,MAAa,kBAEhC,IAAMC,EAAa,CAACC,EAAkBC,IACrC,IAAI,IAAI,WAAWL,EAAKI,EAAUC,CAAU,CAAC,EAAE,EDEvD,OAAOC,MAAuC,QEL9C,OAAS,UAAAC,MAAc,yBAEvB,OAAkB,aAAAC,MAAiB,QCF5B,IAAMC,EAAW,8BACXC,EAAU,GAAGD,CAAQ,QAErBE,EAAS,CACrB,yBAA0B,GAAGF,CAAQ,mBACrC,0BAA2B,GAAGA,CAAQ,mBACvC,EDKO,IAAMG,EAAuB,CACnCC,EACAC,IACI,CACJN,EAAU,IAAM,CACf,IAAMO,EAAUR,EAAO,WAAW,EAElC,GAAIO,EAAU,OAEd,IAAME,EAAWC,GAA4B,CAC5C,IAAMC,EAAqB,OAAO,QAAQL,EAAU,KAAK,EACvD,IAAI,CAAC,CAACM,EAAIC,CAAK,IAAM,CACrB,IAAMC,EAAgBC,EAAiBF,EAAOH,CAAO,EACrD,GAAKI,EACL,MAAO,CACN,GAAAF,EACA,GAAGE,CACJ,CACD,CAAC,EACA,OAAQD,GAAU,CAAC,CAACA,CAAK,EAE3BL,EAAQ,KAAKJ,EAAO,0BAA2BO,CAAkB,CAClE,EAEA,OAAAH,EAAQ,GAAGJ,EAAO,yBAA0BK,CAAO,EAE5C,IAAM,CACZD,EAAQ,IAAIJ,EAAO,yBAA0BK,CAAO,CACrD,CACD,CAAC,CACF,EAEaO,EAAiD,CAC7DC,EACAC,KAGAb,EAAqBa,EAAI,WAAW,WAAYA,EAAI,WAAa,MAAM,EAChED,EAAQ,GAGHE,EAER,CAAC,CAAE,UAAAb,CAAU,KACjBD,EAAqBC,CAAS,EACvB,MAGFS,EAAmB,CACxBK,EACAC,IAEID,aAAiB,SACbA,EAAMC,CAAM,EAEbD,EFvDD,IAAME,EAAa,CACzB,WAAY,CACX,MAAO,CACN,mBAAoB,CAAC,CAAE,WAAAxB,EAAY,SAAAD,CAAS,IAAM,CACjD,GAAI,CAACA,EAAU,OACf,IAAM0B,EAAgBzB,EAAW,QAAQ,mBAAoB,MAAM,EAC7D0B,EAAmB5B,EAAWC,EAAU0B,CAAa,EAC3D,MAAO,CACN,MAAOA,EACP,KAAM,YAAYC,EAAiB,IAAI,GACvC,KAAM,YACP,CACD,EACA,eAAgB,CAAC,CAAE,WAAA1B,EAAY,SAAAD,CAAS,IAAM,CAC7C,GAAI,CAACA,EAAU,OAEf,IAAM4B,EAAO,YADG7B,EAAWC,EAAUC,CAAU,EACd,IAAI,GACrC,MAAO,CACN,MAAOA,EACP,KAAA2B,EACA,KAAM,YACP,CACD,EACA,mBAAoB,CACnB,MAAO,+BACP,KAAM,0DACN,MAAO,OAAO,iBACd,KAAM,UACP,CACD,CACD,EACA,KAAM,CACL,UAAW,CAAC,CACX,SAAAC,EACA,GAAGC,CACJ,IAAuD,CACtD,GAAM,CAAE,mBAAAC,CAAmB,EAAID,EAAM,QAErC,OACC5B,EAAA,cAACP,EAAA,CAAe,GAAGmC,GACjBD,EACAE,EAAmB,YACnB7B,EAAA,cAACoB,EAAA,CACA,UAAWS,EAAmB,WAAW,WAC1C,CAEF,CAEF,CACD,CACD,EAEaC,EAAwC,CAACb,CAAqB","sourcesContent":["import { DocsContainer, type DocsContainerProps } from \"@storybook/blocks\";\nimport type { Addon_DecoratorFunction, Renderer } from \"@storybook/types\";\nimport { getFileUrl } from \"./linkUtil\";\nimport type { SourceLinkParameter } from \"./types\";\n\nimport React, { type PropsWithChildren } from \"react\";\nimport {\n\tParameterResolver,\n\twithParameterResolver,\n} from \"./preview/parameterResolver\";\n\nexport const parameters = {\n\tsourceLink: {\n\t\tlinks: {\n\t\t\t\"component-vscode\": ({ importPath, rootPath }) => {\n\t\t\t\tif (!rootPath) return undefined;\n\t\t\t\tconst componentPath = importPath.replace(/\\.stories\\.tsx?$/, \".tsx\");\n\t\t\t\tconst componentFileUrl = getFileUrl(rootPath, componentPath);\n\t\t\t\treturn {\n\t\t\t\t\tlabel: componentPath,\n\t\t\t\t\thref: `vscode://${componentFileUrl.href}`,\n\t\t\t\t\ticon: \"VSCodeIcon\",\n\t\t\t\t};\n\t\t\t},\n\t\t\t\"story-vscode\": ({ importPath, rootPath }) => {\n\t\t\t\tif (!rootPath) return undefined;\n\t\t\t\tconst fileUrl = getFileUrl(rootPath, importPath);\n\t\t\t\tconst href = `vscode://${fileUrl.href}`;\n\t\t\t\treturn {\n\t\t\t\t\tlabel: importPath,\n\t\t\t\t\thref,\n\t\t\t\t\ticon: \"VSCodeIcon\",\n\t\t\t\t};\n\t\t\t},\n\t\t\t\"addon-powered-by\": {\n\t\t\t\tlabel: \"Powered by addon-source-link\",\n\t\t\t\thref: \"https://github.com/elecdeer/storybook-addon-source-link\",\n\t\t\t\torder: Number.MAX_SAFE_INTEGER,\n\t\t\t\ticon: \"InfoIcon\",\n\t\t\t},\n\t\t},\n\t} satisfies SourceLinkParameter,\n\tdocs: {\n\t\tcontainer: ({\n\t\t\tchildren,\n\t\t\t...props\n\t\t}: PropsWithChildren<DocsContainerProps<Renderer>>) => {\n\t\t\tconst { projectAnnotations } = props.context;\n\n\t\t\treturn (\n\t\t\t\t<DocsContainer {...props}>\n\t\t\t\t\t{children}\n\t\t\t\t\t{projectAnnotations.parameters && (\n\t\t\t\t\t\t<ParameterResolver\n\t\t\t\t\t\t\tparameter={projectAnnotations.parameters.sourceLink}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</DocsContainer>\n\t\t\t);\n\t\t},\n\t},\n};\n\nexport const decorators: Addon_DecoratorFunction[] = [withParameterResolver];\n","import { join, normalize, parse } from \"@std/path/posix\";\n\nexport const getFileUrl = (rootPath: string, importPath: string) => {\n\treturn new URL(`file:///${join(rootPath, importPath)}`);\n};\n\n/**\n * Joins path segments into a path.\n */\nexport const joinPath = (...paths: [string, ...string[]]): string => {\n\treturn join(...paths);\n};\n\n/**\n * Parses a path into an object.\n *\n * @example \"./src/stories/Button.stories.tsx\"\n * -> { root: \"\", dir: \"./src/stories\", base: \"Button.stories.tsx\", ext: \".tsx\", name: \"Button.stories\" }\n */\nexport const parsePath = (path: string) => {\n\treturn parse(path);\n};\n\n/**\n * Normalizes a path, resolving `.` and `..` segments.\n *\n * @example \"./src/stories/Button.stories.tsx\" -> \"src/stories/Button.stories.tsx\"\n */\nexport const normalizePath = (path: string) => {\n\treturn normalize(path);\n};\n","import { addons } from \"@storybook/preview-api\";\nimport type { Addon_DecoratorFunction } from \"@storybook/types\";\nimport { type FC, useEffect } from \"react\";\nimport { EVENTS } from \"../constants\";\nimport type {\n\tLinkEntry,\n\tResolvable,\n\tResolveContext,\n\tSourceLinkParameter,\n} from \"../types\";\n\nexport const useParameterResolver = (\n\tparameter: SourceLinkParameter,\n\tdisabled?: boolean,\n) => {\n\tuseEffect(() => {\n\t\tconst channel = addons.getChannel();\n\n\t\tif (disabled) return;\n\n\t\tconst handler = (context: ResolveContext) => {\n\t\t\tconst resolvedParameters = Object.entries(parameter.links)\n\t\t\t\t.map(([id, entry]) => {\n\t\t\t\t\tconst resolvedEntry = resolveLinkEntry(entry, context);\n\t\t\t\t\tif (!resolvedEntry) return undefined;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\t...resolvedEntry,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((entry) => !!entry);\n\n\t\t\tchannel.emit(EVENTS.RESPONSE_RESOLVABLE_PARAM, resolvedParameters);\n\t\t};\n\n\t\tchannel.on(EVENTS.REQUEST_RESOLVABLE_PARAM, handler);\n\n\t\treturn () => {\n\t\t\tchannel.off(EVENTS.REQUEST_RESOLVABLE_PARAM, handler);\n\t\t};\n\t});\n};\n\nexport const withParameterResolver: Addon_DecoratorFunction = (\n\tStoryFn,\n\tctx,\n) => {\n\t// if the viewMode is docs, the source link is resolved in ParameterResolver\n\tuseParameterResolver(ctx.parameters.sourceLink, ctx.viewMode === \"docs\");\n\treturn StoryFn();\n};\n\nexport const ParameterResolver: FC<{\n\tparameter: SourceLinkParameter;\n}> = ({ parameter }) => {\n\tuseParameterResolver(parameter);\n\treturn null;\n};\n\nconst resolveLinkEntry = (\n\tvalue: Resolvable<LinkEntry>,\n\tparams: ResolveContext,\n): LinkEntry => {\n\tif (value instanceof Function) {\n\t\treturn value(params);\n\t}\n\treturn value;\n};\n","export const ADDON_ID = \"storybook-addon-source-link\";\nexport const TOOL_ID = `${ADDON_ID}/tool`;\n\nexport const EVENTS = {\n\tREQUEST_RESOLVABLE_PARAM: `${ADDON_ID}/request-resolve`,\n\tRESPONSE_RESOLVABLE_PARAM: `${ADDON_ID}/response-resolve`,\n} as const;\n"]}