Skip to content

Commit 755fd68

Browse files
i-am-the-slimetajo
andauthored
Allow stories exported in export blocks (#556)
Co-authored-by: Vojtech Miksu <[email protected]>
1 parent 55de74a commit 755fd68

File tree

3 files changed

+110
-48
lines changed

3 files changed

+110
-48
lines changed

.changeset/breezy-rice-run.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ladle/react": patch
3+
---
4+
5+
Allow export of stories in export blocks

packages/ladle/lib/cli/vite-plugin/parse/get-named-exports.js

+71-48
Original file line numberDiff line numberDiff line change
@@ -24,59 +24,82 @@ const getNamedExports = (
2424
astPath,
2525
) => {
2626
/**
27-
* @type {string}
27+
* @param {any} namedExportDeclaration
28+
* @param {string} namedExport
29+
* @returns {import('../../../shared/types').StoryEntry} result
2830
*/
29-
let namedExport = "";
30-
const namedExportDeclaration = astPath.node.declaration;
31-
if (namedExportDeclaration.type === "ClassDeclaration") {
32-
namedExport = namedExportDeclaration.id.name;
33-
} else if (namedExportDeclaration.type === "VariableDeclaration") {
34-
namedExport = namedExportDeclaration.declarations[0].id.name;
35-
} else if (namedExportDeclaration.type === "FunctionDeclaration") {
36-
namedExport = namedExportDeclaration.id.name;
37-
} else {
38-
throw new Error(
39-
`Named export in ${entry} must be variable, class or function.`,
40-
);
41-
}
31+
const namedExportToStory = (namedExportDeclaration, namedExport) => {
32+
if (namedExport.includes("__")) {
33+
throw new Error(
34+
`Story named ${namedExport} can't contain "__". It's reserved for internal encoding. Please rename this export.`,
35+
);
36+
}
4237

43-
if (namedExport.includes("__")) {
44-
throw new Error(
45-
`Story named ${namedExport} can't contain "__". It's reserved for internal encoding. Please rename this export.`,
38+
let storyNamespace = fileId;
39+
if (exportDefaultProps && exportDefaultProps.title) {
40+
storyNamespace = titleToFileId(exportDefaultProps.title);
41+
}
42+
const storyName = namedExportToStoryName[namedExport]
43+
? namedExportToStoryName[namedExport]
44+
: namedExport;
45+
const storyId = `${kebabCase(
46+
storyNamespace,
47+
)}${storyDelimiter}${storyDelimiter}${kebabCase(storyName)}`;
48+
// attach default meta to each story
49+
if (exportDefaultProps && exportDefaultProps.meta) {
50+
storyParams[storyId] = exportDefaultProps;
51+
}
52+
// add and merge story specific meta
53+
if (namedExportToMeta[namedExport]) {
54+
storyParams[storyId] = merge(cloneDeep(storyParams[storyId] || {}), {
55+
meta: namedExportToMeta[namedExport],
56+
});
57+
}
58+
const componentName = getEncodedStoryName(
59+
kebabCase(storyNamespace),
60+
kebabCase(storyName),
4661
);
47-
}
62+
const story = {
63+
storyId,
64+
componentName,
65+
namedExport,
66+
locStart: namedExportDeclaration.loc.start.line,
67+
locEnd: namedExportDeclaration.loc.end.line,
68+
};
69+
return story;
70+
};
4871

49-
let storyNamespace = fileId;
50-
if (exportDefaultProps && exportDefaultProps.title) {
51-
storyNamespace = titleToFileId(exportDefaultProps.title);
52-
}
53-
const storyName = namedExportToStoryName[namedExport]
54-
? namedExportToStoryName[namedExport]
55-
: namedExport;
56-
const storyId = `${kebabCase(
57-
storyNamespace,
58-
)}${storyDelimiter}${storyDelimiter}${kebabCase(storyName)}`;
59-
// attach default meta to each story
60-
if (exportDefaultProps && exportDefaultProps.meta) {
61-
storyParams[storyId] = exportDefaultProps;
62-
}
63-
// add and merge story specific meta
64-
if (namedExportToMeta[namedExport]) {
65-
storyParams[storyId] = merge(cloneDeep(storyParams[storyId] || {}), {
66-
meta: namedExportToMeta[namedExport],
67-
});
72+
/**
73+
* @type {string}
74+
*/
75+
// Inline exports, such as: export const Story = () => <h1>Export List</h1>;
76+
if (astPath.node?.declaration?.type) {
77+
let namedExport = "";
78+
const namedExportDeclaration = astPath.node?.declaration;
79+
if (namedExportDeclaration.type === "ClassDeclaration") {
80+
namedExport = namedExportDeclaration.id.name;
81+
} else if (namedExportDeclaration.type === "VariableDeclaration") {
82+
namedExport = namedExportDeclaration.declarations[0].id.name;
83+
} else if (namedExportDeclaration.type === "FunctionDeclaration") {
84+
namedExport = namedExportDeclaration.id.name;
85+
} else {
86+
throw new Error(
87+
`Named export in ${entry} must be variable, class or function.`,
88+
);
89+
}
90+
const story = namedExportToStory(namedExportDeclaration, namedExport);
91+
stories.push(story);
92+
} else if (astPath.node?.specifiers.length > 0) {
93+
// It's an export block export, such as: { story, story as storyRenamed };
94+
astPath.node?.specifiers.forEach(
95+
/** type * @param {any} specifier */
96+
(specifier) => {
97+
const namedExport = specifier.exported.name;
98+
const story = namedExportToStory(specifier, namedExport);
99+
stories.push(story);
100+
},
101+
);
68102
}
69-
const componentName = getEncodedStoryName(
70-
kebabCase(storyNamespace),
71-
kebabCase(storyName),
72-
);
73-
stories.push({
74-
storyId,
75-
componentName,
76-
namedExport,
77-
locStart: namedExportDeclaration.loc.start.line,
78-
locEnd: namedExportDeclaration.loc.end.line,
79-
});
80103
};
81104

82105
export default getNamedExports;

packages/ladle/tests/parse/get-named-exports.test.ts

+34
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,37 @@ test("Function Named Export", async () => {
283283
}),
284284
);
285285
});
286+
287+
test("Export Block", async () => {
288+
expect(
289+
parseWithFn(
290+
`var myStory = () => "Text";
291+
export {
292+
myStory,
293+
myStory as renamed
294+
};`,
295+
{},
296+
getNamedExports,
297+
"ExportNamedDeclaration",
298+
),
299+
).toEqual(
300+
getOutput({
301+
stories: [
302+
{
303+
componentName: "file$$my$story",
304+
namedExport: "myStory",
305+
storyId: "file--my-story",
306+
locEnd: 3,
307+
locStart: 3,
308+
},
309+
{
310+
componentName: "file$$renamed",
311+
namedExport: "renamed",
312+
storyId: "file--renamed",
313+
locEnd: 4,
314+
locStart: 4,
315+
},
316+
],
317+
}),
318+
);
319+
});

0 commit comments

Comments
 (0)