diff --git a/src/loaders/mdx-loader.ts b/src/loaders/mdx-loader.ts index a8a8bc8bf..190b974b8 100644 --- a/src/loaders/mdx-loader.ts +++ b/src/loaders/mdx-loader.ts @@ -19,7 +19,7 @@ import { mdx } from '@mdx-js/react'; export default async function mdxLoader(this: Rsg.StyleguidistLoaderContext, content: string) { const callback = this.async() || (() => ''); const { updateExample, context } = this._styleguidist; - const { component } = loaderUtils.getOptions(this) || {}; + const { component: componentPath } = loaderUtils.getOptions(this) || {}; let result; try { @@ -29,16 +29,13 @@ export default async function mdxLoader(this: Rsg.StyleguidistLoaderContext, con addReact, markStaticExamples, addExampleIndicies, - updateExamples({ updateExample, resourcePath: this.resourcePath }), + updateExamples({ updateExample, mdxDocumentPath: this.resourcePath }), exportExamples, // Sections don't have current components - component && exportStories({ component, resourcePath: this.resourcePath }), + componentPath && exportStories({ componentPath, mdxDocumentPath: this.resourcePath }), provideDocumentScope({ context }), provideExampleScope, - component && provideCurrentComponent({ component }), - // TODO: Use smart (no-duplicates) insert instead of deduplication - // TODO: Or better insert needed imports in front of each example - // deduplicateImports, + componentPath && provideCurrentComponent({ componentPath }), ], }); } catch (err) { diff --git a/src/loaders/rehype/__tests__/exportStories.spec.ts b/src/loaders/rehype/__tests__/exportStories.spec.ts index 45dfa1abf..d7736061a 100644 --- a/src/loaders/rehype/__tests__/exportStories.spec.ts +++ b/src/loaders/rehype/__tests__/exportStories.spec.ts @@ -15,7 +15,9 @@ const compile = async (mdxContent: string, storyContent: string) => { vol.fromJSON({ '/Pizza/Pizza.stories.tsx': storyContent }); const result = await mdx(mdxContent, { filepath: 'Pizza.md', - rehypePlugins: [exportStories({ component: 'Pizza', resourcePath: '/Pizza/Pizza.md' })], + rehypePlugins: [ + exportStories({ componentPath: './index.tsx', mdxDocumentPath: '/Pizza/Readme.md' }), + ], }); // Strip repeated parts @@ -182,6 +184,35 @@ export const basic = () => `); }); + test('skips current component (named import)', async () => { + const result = await compile( + `Henlo`, + ` +import { Pizza } from '.'; +import Container from './Container'; +export const basic = () => +` + ); + expect(result).toMatchInlineSnapshot(` + " + import * as __story_import_0 from '.' + import * as __story_import_1 from './Container' + export const __namedExamples = { + 'basic': 'import Container from \\\\'./Container\\\\';\\\\n\\\\n' + }; + export const __storiesScope = { + '.': __story_import_0, + './Container': __story_import_1 + }; + + const layoutProps = { + __namedExamples, + __storiesScope + }; + " + `); + }); + test('includes the current component when it is not the only import', async () => { const result = await compile( `Henlo`, diff --git a/src/loaders/rehype/exportStories.ts b/src/loaders/rehype/exportStories.ts index fad94b166..61056fe28 100644 --- a/src/loaders/rehype/exportStories.ts +++ b/src/loaders/rehype/exportStories.ts @@ -21,6 +21,7 @@ import toAst from 'to-ast'; import { builders as b } from 'ast-types'; import getAst from '../utils/getAst'; import { ModuleMap } from './types'; +import getNameFromFilePath from '../utils/getNameFromFilePath'; // TODO: Hot reload // TODO: Unindenting @@ -209,12 +210,12 @@ const getExportCode = (node: Declaration, code: string) => { const prependExampleWithDependencies = ( code: string, dependencies: Dependency[], - component: string + componentName: string ) => { const usedDependencies = dependencies.filter( (dependency) => // Ignore the current component import it it's the only import - !(dependency.names.length === 1 && dependency.names[0] === component) && + !(dependency.names.length === 1 && dependency.names[0] === componentName) && // Include imports when any of the names are present in the code dependency.names.some((name) => code.match(new RegExp(`\\b${name}\\b`))) ); @@ -229,7 +230,7 @@ const prependExampleWithDependencies = ( .join('\n\n'); }; -const getExports = (ast: Program, code: string, component: string) => { +const getExports = (ast: Program, code: string, componentName: string) => { const imports = getImportStatements(ast, code); const variables = getVariableStatements(ast, code); @@ -245,7 +246,7 @@ const getExports = (ast: Program, code: string, component: string) => { exports[exportCode.name] = prependExampleWithDependencies( exportCode.code, [...imports, ...variables], - component + componentName ); } } @@ -275,12 +276,16 @@ const getExports = (ast: Program, code: string, component: string) => { * './Button': __story_import_0 * } */ -export default ({ component, resourcePath }: { component: string; resourcePath: string }) => () => ( - treeRaw: MdxNode -) => { +export default ({ + componentPath, + mdxDocumentPath, +}: { + componentPath: string; + mdxDocumentPath: string; +}) => () => (treeRaw: MdxNode) => { const tree = treeRaw as Parent; - const componentAbsolutePath = path.resolve(path.dirname(resourcePath), component); + const componentAbsolutePath = path.resolve(path.dirname(mdxDocumentPath), componentPath); const storiesFile = getStoriesFile(componentAbsolutePath); if (!storiesFile) { return tree; @@ -292,8 +297,10 @@ export default ({ component, resourcePath }: { component: string; resourcePath: return tree; } + const componentName = getNameFromFilePath(componentAbsolutePath); + // Generate export for named examples - const exports = getExports(storiesAst, storiesCode, component); + const exports = getExports(storiesAst, storiesCode, componentName); const examplesExportCode = `export const __namedExamples = ${generate(toAst(exports))}`; tree.children.push({ type: 'export', diff --git a/src/loaders/rehype/provideCurrentComponent.ts b/src/loaders/rehype/provideCurrentComponent.ts index b61252b25..7dd4efed4 100644 --- a/src/loaders/rehype/provideCurrentComponent.ts +++ b/src/loaders/rehype/provideCurrentComponent.ts @@ -9,13 +9,13 @@ const getLocalName = () => `__ex_component`; * import * as __ex_component from './Button' * export const __currentComponent = __ex_component */ -export default ({ component }: { component: string }) => () => (treeRaw: Node) => { +export default ({ componentPath }: { componentPath: string }) => () => (treeRaw: Node) => { const tree = treeRaw as Parent; // Generate import tree.children.push({ type: 'import', - value: `import * as ${getLocalName()} from '${component}'`, + value: `import * as ${getLocalName()} from '${componentPath}'`, }); // Generate export diff --git a/src/loaders/rehype/updateExamples.ts b/src/loaders/rehype/updateExamples.ts index 9d217eee7..30a83d8d8 100644 --- a/src/loaders/rehype/updateExamples.ts +++ b/src/loaders/rehype/updateExamples.ts @@ -17,17 +17,17 @@ const getCode = (node: CodeNode): string => */ export default ({ updateExample, - resourcePath, + mdxDocumentPath, }: { updateExample: Rsg.SanitizedStyleguidistConfig['updateExample']; - resourcePath: string; + mdxDocumentPath: string; }) => () => (tree: Node) => { visit(tree, { type: 'element', tagName: 'code' }, (node) => { const content = getCode(node); const lang = getCodeLang(node); const { className, metastring, ...settings } = node.properties; - const nextExample = updateExample({ content, lang, settings }, resourcePath); + const nextExample = updateExample({ content, lang, settings }, mdxDocumentPath); // TODO: Update lang node.children[0].value = nextExample.content.trim();