构建时故事标题生成

一个 Babel 插件,用于在编译时为 Storybook CSF 故事生成标题,通常基于故事文件的名称。

在 Github 上查看

babel-plugin-storybook-csf-title

Atlassian license PRs Welcome

一个 Babel 插件,用于在编译时Storybook CSF 故事生成标题,通常基于故事文件的名称。

用法

该插件根据要作为插件选项提供的 toTitle 函数的结果,为所有转换后的文件添加一个 title 属性。

假设 toTitle: () => 'foo',有三种一般场景

1️⃣ 文件未提供默认导出

在这种情况下,该插件创建一个默认导出 { title: "foo" }

例如:

import React from 'react';
import Component from './index';

export const Example = () => <Component />;

被转换为

import React from 'react';
import Component from './index';

export const Example = () => <Component />;

export default { title: 'foo' };

2️⃣ 文件提供一个对象作为其默认导出

在这种情况下,该插件将 title: foo 属性添加到现有导出。

例如:

import React from 'react';
import Component from './index';

export default { 
    something: 'something'
};

export const Example = () => <Component />;

被转换为

import React from 'react';
import Component from './index';

export default { 
    title: 'foo'
    something: 'something'
};

export const Example = () => <Component />;

如果现有导出已经包含 title 属性,则会抛出错误。

3️⃣ 文件提供一个非对象作为其默认导出

如果设置了 renameDefaultExportsTo 选项,该插件会假设默认导出是一个组件,并将该组件移动到一个名为 ${renameDefaultExportsTo} 的命名导出。然后它创建一个默认导出 { title: "foo" }

例如,假设 renameDefaultExportsTo"Default"

import React from 'react';
import Component from './index';

export default () => <Component />;

被转换为

import React from 'react';
import Component from './index';

export const Default = () => <Component />;

export default { 
    title: 'foo'
};

如果 ${renameDefaultExportsTo} 导出已经存在,则会抛出错误。

安装

安装该插件,例如通过 yarn

yarn add --dev babel-plugin-storybook-csf-title

在您的 Babel 配置中,添加 babel-plugin-storybook-csf-title 作为插件

plugins: [
    ['babel-plugin-storybook-csf-title', { toTitle: require('./your-to-title-function') }],
]

请注意,该插件实际上只对故事文件有意义。您需要确保它只应用于这些文件,例如像这样

/* add plugin to babel, however disable it by default */
plugins: [
    ['babel-plugin-storybook-csf-title', false], 
],
/* enable the plugin for all files that match your story name pattern */
overrides: [{ 
    include: /\/stories\.(ts|tsx)$/, 
    plugins: [
        ['babel-plugin-storybook-csf-title', { toTitle: require('./your-to-title-function') }]
    ]
}]

选项

该插件接受三个选项,toTitle(必需)、ifTitleFound(可选)和 renameDefaultExportsTo(可选)

  • toTitle 是一个函数,它为每个被转换的故事文件接收 Babel 的 state 对象,并且必须以字符串形式返回故事文件的标题。大多数 toTitle 实现将根据 state.filename 做出决定。

  • ifTitleFound 是一个可选的字符串值,可以设置为

    • 'skip' - 如果标题已经手动在代码中指定,则跳过添加标题
    • undefined(或任何其他值) - 如果处理一个已经定义了标题的文件,则引发错误
  • renameDefaultExportsTo 是一个可选的字符串值,用于控制上面描述的场景 3。默认情况下它为 undefined

生成有意义的故事名称

在大多数情况下,故事名称将根据故事文件的名称生成。以下是一个针对 yarn workspaces 样式单仓库设置的 toTitle 的可能实现

const path = require('path');
const pkgUp = require('pkg-up');

module.exports = (state) => {

    // find the closest package.json
    const packageJsonPath = pkgUp.sync({ cwd: state.filename });

    // read the package.json
    const packageJson = require(packageJsonPath);

    // get the path of the story file relative to the package root
    const { dir: packageJsonDir } = path.parse(packageJsonPath);
    const { dir: fileDir, name: fileName } = path.parse(path.relative(packageJsonDir, state.filename));

    const storybookPath = [
        // package name; "/" has meaning to storybook, hence replace a possible "/" by "|"
        packageJson.name.replace('/', '|'),

        // file dir
        ...fileDir.split(path.sep),
    ];

    // handle file names
    if (fileName === 'examples' || fileName === 'stories') {
        // nothing to do
    } else if (fileName.endsWith('.stories')) {
        storybookPath.push(fileName.slice(0, '.stories'.length + 1));
    } else if (fileName.endsWith('.examples')) {
        storybookPath.push(fileName.slice(0, '.examples'.length + 1));
    }

    return storybookPath.join('/');
}

贡献

欢迎对 babel-plugin-storybook-csf-title 进行贡献!请参阅 CONTRIBUTING.md 了解详细信息。

许可证

版权所有 (c) 2020 Atlassian 及其他人。Apache 2.0 许可,请参阅 LICENSE 文件。

With ❤️ from Atlassian