Skip to content

Commit

Permalink
Source-loader: Support function declaration exports
Browse files Browse the repository at this point in the history
Added support for named exports with function declaration in
source-loader, in order to fix that the story's source code doesn't show
up in Docs mode's code preview.

The changes in generate-helpers.js is necessary because we can't wrap
named export functions:

```
export function foo() {}

// turns into this if we just wrapped it

export wrapper(function foo() {}) // syntax error!
```
  • Loading branch information
pocka committed Dec 7, 2019
1 parent ab6ef53 commit d9fad40
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 20 deletions.
10 changes: 10 additions & 0 deletions __mocks__/inject-decorator.ts.csf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ export const emoji = () => (
</span>
</Button>
);

export function emojiFn() {
return (
<Button onClick={action("clicked")}>
<span role="img" aria-label="so cool">
😀 😎 👍 💯
</span>
</Button>
)
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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\\",\\"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}}}},},
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\\"
};
Expand All @@ -19,6 +19,16 @@ export const emoji = addSourceDecorator(() => (
😀 😎 👍 💯
</span>
</Button>
), {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});
), {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});;
export const emojiFn = addSourceDecorator(function emojiFn() {
return (
<Button onClick={action(\\"clicked\\")}>
<span role=\\"img\\" aria-label=\\"so cool\\">
😀 😎 👍 💯
</span>
</Button>
)
}, {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});
"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ const STORY_DECORATOR_STATEMENT =
const ADD_PARAMETERS_STATEMENT =
'.addParameters({ storySource: { source: __STORY__, locationsMap: __ADDS_MAP__ } })';
const applyExportDecoratorStatement = part =>
` addSourceDecorator(${part}, {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});`;
part.declaration.isVariableDeclaration
? ` addSourceDecorator(${part.source}, {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});`
: ` const ${part.declaration.ident} = addSourceDecorator(${part.source}, {__STORY__, __ADDS_MAP__,__MAIN_FILE_LOCATION__,__MODULE_DEPENDENCIES__,__LOCAL_DEPENDENCIES__,__SOURCE_PREFIX__,__IDS_TO_FRAMEWORKS__});`;

export function generateSourceWithDecorators(source, ast, withParameters) {
const { comments = [] } = ast;
Expand All @@ -91,7 +93,7 @@ export function generateSourceWithDecorators(source, ast, withParameters) {
const partsUsingExports = splitExports(ast, source);

const newSource = partsUsingExports
.map((part, i) => (i % 2 === 0 ? part : applyExportDecoratorStatement(part)))
.map((part, i) => (i % 2 === 0 ? part.source : applyExportDecoratorStatement(part)))
.join('');

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ export function splitExports(ast, source) {
fallback: 'iteration',
enter: node => {
patchNode(node);
if (
node.type === 'ExportNamedDeclaration' &&
node.declaration &&

const isNamedExport = node.type === 'ExportNamedDeclaration' && node.declaration;

const isFunctionVariableExport =
isNamedExport &&
node.declaration.declarations &&
node.declaration.declarations.length === 1 &&
node.declaration.declarations[0].type === 'VariableDeclarator' &&
Expand All @@ -39,17 +41,36 @@ export function splitExports(ast, source) {
node.declaration.declarations[0].init &&
['CallExpression', 'ArrowFunctionExpression', 'FunctionExpression'].includes(
node.declaration.declarations[0].init.type
)
) {
const functionNode = node.declaration.declarations[0].init;
parts.push(source.substring(lastIndex, functionNode.start - 1));
parts.push(source.substring(functionNode.start, functionNode.end));
);

const isFunctionDeclarationExport =
isNamedExport &&
node.declaration.type === 'FunctionDeclaration' &&
node.declaration.id &&
node.declaration.id.name;

if (isFunctionDeclarationExport || isFunctionVariableExport) {
const functionNode = isFunctionVariableExport
? node.declaration.declarations[0].init
: node.declaration;
parts.push({
source: source.substring(lastIndex, functionNode.start - 1),
});
parts.push({
source: source.substring(functionNode.start, functionNode.end),
declaration: {
isVariableDeclaration: isFunctionVariableExport,
ident: isFunctionVariableExport
? node.declaration.declarations[0].id.name
: functionNode.id.name,
},
});
lastIndex = functionNode.end;
}
},
});

if (source.length > lastIndex + 1) parts.push(source.substring(lastIndex + 1));
if (source.length > lastIndex + 1) parts.push({ source: source.substring(lastIndex + 1) });
if (parts.length === 1) return [source];
return parts;
}
Expand Down Expand Up @@ -110,9 +131,11 @@ export function findExportsMap(ast) {
fallback: 'iteration',
enter: node => {
patchNode(node);
if (
node.type === 'ExportNamedDeclaration' &&
node.declaration &&

const isNamedExport = node.type === 'ExportNamedDeclaration' && node.declaration;

const isFunctionVariableExport =
isNamedExport &&
node.declaration.declarations &&
node.declaration.declarations.length === 1 &&
node.declaration.declarations[0].type === 'VariableDeclarator' &&
Expand All @@ -121,10 +144,24 @@ export function findExportsMap(ast) {
node.declaration.declarations[0].init &&
['CallExpression', 'ArrowFunctionExpression', 'FunctionExpression'].includes(
node.declaration.declarations[0].init.type
)
) {
const storyName = storyNameFromExport(node.declaration.declarations[0].id.name);
const toAdd = handleExportedName(title, storyName, node.declaration.declarations[0].init);
);

const isFunctionDeclarationExport =
isNamedExport &&
node.declaration.type === 'FunctionDeclaration' &&
node.declaration.id &&
node.declaration.id.name;

if (isFunctionDeclarationExport || isFunctionVariableExport) {
const exportDeclaration = isFunctionVariableExport
? node.declaration.declarations[0]
: node.declaration;
const storyName = storyNameFromExport(exportDeclaration.id.name);
const toAdd = handleExportedName(
title,
storyName,
exportDeclaration.init || exportDeclaration
);
Object.assign(addsMap, toAdd);
}
},
Expand Down

0 comments on commit d9fad40

Please sign in to comment.