Skip to content

Commit

Permalink
Source-loader: Handle includeStories/excludeStories in CSF
Browse files Browse the repository at this point in the history
  • Loading branch information
shilman committed Dec 9, 2019
1 parent 02e8186 commit 971809d
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 21 deletions.
4 changes: 3 additions & 1 deletion __mocks__/inject-decorator.ts.csf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { action } from "@storybook/addon-actions";
import { Button } from "@storybook/react/demo";

export default {
title: "Button"
title: "Button",
excludeStories: ["text"],
includeStories: /emoji.*/
};

export const text = () => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ exports[`inject-decorator positive - ts - csf includes storySource parameter in
import { action } from \\"@storybook/addon-actions\\";
import { Button } from \\"@storybook/react/demo\\";
export default {parameters: {\\"storySource\\":{\\"source\\":\\"import React from \\\\\\"react\\\\\\";\\\\nimport { action } from \\\\\\"@storybook/addon-actions\\\\\\";\\\\nimport { Button } from \\\\\\"@storybook/react/demo\\\\\\";\\\\n\\\\nexport default {\\\\n title: \\\\\\"Button\\\\\\"\\\\n};\\\\n\\\\nexport const text = () => (\\\\n <Button onClick={action(\\\\\\"clicked\\\\\\")}>Hello Button</Button>\\\\n);\\\\n\\\\nexport const emoji = () => (\\\\n <Button onClick={action(\\\\\\"clicked\\\\\\")}>\\\\n <span role=\\\\\\"img\\\\\\" aria-label=\\\\\\"so cool\\\\\\">\\\\n 😀 😎 👍 💯\\\\n </span>\\\\n </Button>\\\\n);\\\\n\\\\nexport function emojiFn() {\\\\n return (\\\\n <Button onClick={action(\\\\\\"clicked\\\\\\")}>\\\\n <span role=\\\\\\"img\\\\\\" aria-label=\\\\\\"so cool\\\\\\">\\\\n 😀 😎 👍 💯\\\\n </span>\\\\n </Button>\\\\n )\\\\n};\\\\n\\",\\"locationsMap\\":{\\"button--text\\":{\\"startLoc\\":{\\"col\\":20,\\"line\\":9},\\"endLoc\\":{\\"col\\":1,\\"line\\":11},\\"startBody\\":{\\"col\\":20,\\"line\\":9},\\"endBody\\":{\\"col\\":1,\\"line\\":11}},\\"button--emoji\\":{\\"startLoc\\":{\\"col\\":21,\\"line\\":13},\\"endLoc\\":{\\"col\\":1,\\"line\\":19},\\"startBody\\":{\\"col\\":21,\\"line\\":13},\\"endBody\\":{\\"col\\":1,\\"line\\":19}},\\"button--emoji-fn\\":{\\"startLoc\\":{\\"col\\":7,\\"line\\":21},\\"endLoc\\":{\\"col\\":1,\\"line\\":29},\\"startBody\\":{\\"col\\":7,\\"line\\":21},\\"endBody\\":{\\"col\\":1,\\"line\\":29}}}},},
title: \\"Button\\"
export default {parameters: {\\"storySource\\":{\\"source\\":\\"import React from \\\\\\"react\\\\\\";\\\\nimport { action } from \\\\\\"@storybook/addon-actions\\\\\\";\\\\nimport { Button } from \\\\\\"@storybook/react/demo\\\\\\";\\\\n\\\\nexport default {\\\\n title: \\\\\\"Button\\\\\\",\\\\n excludeStories: [\\\\\\"text\\\\\\"],\\\\n includeStories: /emoji.*/\\\\n};\\\\n\\\\nexport const text = () => (\\\\n <Button onClick={action(\\\\\\"clicked\\\\\\")}>Hello Button</Button>\\\\n);\\\\n\\\\nexport const emoji = () => (\\\\n <Button onClick={action(\\\\\\"clicked\\\\\\")}>\\\\n <span role=\\\\\\"img\\\\\\" aria-label=\\\\\\"so cool\\\\\\">\\\\n 😀 😎 👍 💯\\\\n </span>\\\\n </Button>\\\\n);\\\\n\\\\nexport function emojiFn() {\\\\n return (\\\\n <Button onClick={action(\\\\\\"clicked\\\\\\")}>\\\\n <span role=\\\\\\"img\\\\\\" aria-label=\\\\\\"so cool\\\\\\">\\\\n 😀 😎 👍 💯\\\\n </span>\\\\n </Button>\\\\n )\\\\n};\\\\n\\",\\"locationsMap\\":{\\"button--text\\":{\\"startLoc\\":{\\"col\\":20,\\"line\\":11},\\"endLoc\\":{\\"col\\":1,\\"line\\":13},\\"startBody\\":{\\"col\\":20,\\"line\\":11},\\"endBody\\":{\\"col\\":1,\\"line\\":13}},\\"button--emoji\\":{\\"startLoc\\":{\\"col\\":21,\\"line\\":15},\\"endLoc\\":{\\"col\\":1,\\"line\\":21},\\"startBody\\":{\\"col\\":21,\\"line\\":15},\\"endBody\\":{\\"col\\":1,\\"line\\":21}},\\"button--emoji-fn\\":{\\"startLoc\\":{\\"col\\":7,\\"line\\":23},\\"endLoc\\":{\\"col\\":1,\\"line\\":31},\\"startBody\\":{\\"col\\":7,\\"line\\":23},\\"endBody\\":{\\"col\\":1,\\"line\\":31}}}},},
title: \\"Button\\",
excludeStories: [\\"text\\"],
includeStories: /emoji.*/
};
export const text = addSourceDecorator(() => (
export const text = () => (
<Button onClick={action(\\"clicked\\")}>Hello Button</Button>
), {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});;
);
export const emoji = addSourceDecorator(() => (
<Button onClick={action(\\"clicked\\")}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { storyNameFromExport } from '@storybook/csf';
import { storyNameFromExport, isExportStory } from '@storybook/csf';
import { handleADD, handleSTORYOF, patchNode, handleExportedName } from './parse-helpers';

const estraverse = require('estraverse');
Expand All @@ -20,10 +20,79 @@ export function splitSTORYOF(ast, source) {

return parts;
}

function isFunctionVariable(declarations, includeExclude) {
return (
declarations &&
declarations.length === 1 &&
declarations[0].type === 'VariableDeclarator' &&
declarations[0].id &&
declarations[0].id.name &&
declarations[0].init &&
['CallExpression', 'ArrowFunctionExpression', 'FunctionExpression'].includes(
declarations[0].init.type
) &&
isExportStory(declarations[0].id.name, includeExclude)
);
}

function isFunctionDeclaration(declaration, includeExclude) {
return (
declaration.type === 'FunctionDeclaration' &&
declaration.id &&
declaration.id.name &&
isExportStory(declaration.id.name, includeExclude)
);
}

function getDescriptor(metaDeclaration, propertyName) {
const property =
metaDeclaration &&
metaDeclaration.declaration &&
metaDeclaration.declaration.properties.find(p => p.key && p.key.name === propertyName);
if (!property) {
return undefined;
}

const { type } = property.value;

switch (type) {
case 'ArrayExpression':
return property.value.elements.map(t => t.value);
case 'Literal':
return property.value.value;
default:
throw new Error(`Unexpected descriptor: ${type}`);
}
}

function findIncludeExclude(ast) {
const program = (ast && ast.program) || ast;
const metaDeclaration =
program &&
program.body &&
program.body.find(
d =>
d.type === 'ExportDefaultDeclaration' &&
d.declaration.type === 'ObjectExpression' &&
(d.declaration.properties || []).length
);

const includeStories = getDescriptor(metaDeclaration, 'includeStories');
const excludeStories = getDescriptor(metaDeclaration, 'excludeStories');

return {
includeStories,
excludeStories,
};
}

export function splitExports(ast, source) {
const parts = [];
let lastIndex = 0;

const includeExclude = findIncludeExclude(ast);

estraverse.traverse(ast, {
fallback: 'iteration',
enter: node => {
Expand All @@ -32,22 +101,9 @@ export function splitExports(ast, source) {
const isNamedExport = node.type === 'ExportNamedDeclaration' && node.declaration;

const isFunctionVariableExport =
isNamedExport &&
node.declaration.declarations &&
node.declaration.declarations.length === 1 &&
node.declaration.declarations[0].type === 'VariableDeclarator' &&
node.declaration.declarations[0].id &&
node.declaration.declarations[0].id.name &&
node.declaration.declarations[0].init &&
['CallExpression', 'ArrowFunctionExpression', 'FunctionExpression'].includes(
node.declaration.declarations[0].init.type
);

isNamedExport && isFunctionVariable(node.declaration.declarations, includeExclude);
const isFunctionDeclarationExport =
isNamedExport &&
node.declaration.type === 'FunctionDeclaration' &&
node.declaration.id &&
node.declaration.id.name;
isNamedExport && isFunctionDeclaration(node.declaration, includeExclude);

if (isFunctionDeclarationExport || isFunctionVariableExport) {
const functionNode = isFunctionVariableExport
Expand Down

0 comments on commit 971809d

Please sign in to comment.