Rollup & React - 如何分离组件包?

cod*_*sfl 11 javascript rollup reactjs rollupjs

我目前正在尝试为 React 构建一个 UI 库,但遇到了一些麻烦。目前我正在使用打字稿和汇总,我能够捆绑单个组件index.js,并且能够导入这些组件,但它正在导入整个库。

现在

文件结构:

src
--components
-----button
-------button.tsx
-------button.types.ts
-----input
-------input.tsx
-------input.types.ts
-----index.ts
rollup.js
Run Code Online (Sandbox Code Playgroud)

我的汇总目标index.ts将所有内容导出为这样:

src
--components
-----button
-------button.tsx
-------button.types.ts
-----input
-------input.tsx
-------input.types.ts
-----index.ts
rollup.js
Run Code Online (Sandbox Code Playgroud)

我可以像这样导入反应项目:

export { default as Button} from './button/button'
export { default as Input } from './input/input'
Run Code Online (Sandbox Code Playgroud)

我想要做什么 我希望每个组件都单独捆绑,并且像这样导入

import { Button, Input } from 'my-library'
Run Code Online (Sandbox Code Playgroud)

我尝试过的: 阅读文档后,似乎这就是preserveModule: true我正在寻找的内容,但后来我尝试按照上面的方式导入,但它开始抱怨没有找到任何东西。

我当前的 rollup.js 如下所示:

import { Input } from 'my-library/input'
import { Button } from 'my-library/button'
Run Code Online (Sandbox Code Playgroud)

hai*_*y25 12

编辑:我在这里发布了关于媒体的更全面的教程

\n

我尝试使用preserveModules,但它不会为每个组件生成index.js 文件,以便我可以像这样导入:

\n

import Button from \'lib/Button\'

\n

因此,我想出了一个解决办法,使 rollup 循环遍历我的 src 文件夹,为我在 rootDir 的 src 中拥有的每个组件文件夹生成一个带有入口点的文件夹

\n
    \n
  1. 维护严格的文件夹结构,并为每个组件文件夹提供入口点。除了 src 文件夹中没有文件夹的 index.ts 之外,不要有松散的文件。正确命名您的文件夹,就像您希望用户导入文件夹的方式一样
  2. \n
\n

src文件夹结构:

\n
rollup.config.js\nsrc\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Accordion\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Accordion.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionBody.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionButton.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionCollapse.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionContext.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionHeader.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionItem.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionItemContext.ts\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Alert\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Alert.tsx\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Badge\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Badge.tsx\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Breadcrumb\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Breadcrumb.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 BreadcrumbItem.tsx\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Button\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Button.tsx\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ButtonGroup\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ButtonGroup.tsx\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n...\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Tooltip\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Tooltip.tsx\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 TooltipBox.tsx\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.ts\n
Run Code Online (Sandbox Code Playgroud)\n

对于这种情况,维护每个组件文件夹的入口点至关重要。我仍然保留了 src 文件夹的入口点,以便用户仍然可以使用一行从库中导入多个组件\ni.e。import {Button, Accordion, ...} from \'lib\'

\n
    \n
  1. 汇总配置
  2. \n
\n
    \n
  • getFolders 返回用于导出的文件夹名称数组

    \n
  • \n
  • 循环遍历 getFolders 以生成每个文件夹的汇总 obj。

    \n
  • \n
  • 对于打字稿项目,汇总输出已保留文件夹结构的打字文件,因此我意识到文件夹 Accordion、Button 等已经仅包含打字文件。现在我们需要向其中添加index.js 文件!

    \n
  • \n
\n
import peerDepsExternal from \'rollup-plugin-peer-deps-external\';\nimport resolve from \'@rollup/plugin-node-resolve\';\nimport commonjs from \'@rollup/plugin-commonjs\';\nimport typescript from \'rollup-plugin-typescript2\';\nimport replace from \'@rollup/plugin-replace\';\nimport { terser } from \'rollup-plugin-terser\';\nconst packageJson = require(\'./package.json\');\nimport { getFolders } from \'./scripts/buildUtils\';\n\nconst plugins =  [\n    peerDepsExternal(),\n    resolve(),\n    commonjs(),\n    typescript({\n      tsconfig: \'./tsconfig.json\',\n      useTsconfigDeclarationDir: true,\n    }),\n    terser()\n  ]\n\nconst getFolders = (entry) => {\n   // get the names of folders and files of the entry directory\n   const dirs = fs.readdirSync(entry)\n   // do not include folders not meant for export and do not process index.ts\n   const dirsWithoutIndex = dirs.filter(name => name !== \'index.ts\').filter(name => name !== \'utils\')\n   // [\'Accordion\', \'Button\'...]\n   return dirsWithoutIndex\n}\n\n//loop through your folders and generate a rollup obj per folder\nconst folderBuilds = getFolders(\'./src\').map(folder=> {\n  return {\n    input: `src/${folder}/index.ts`, \n    output: {\n      // ensure file destination is same as where the typings are\n      file: `dist/${folder}/index.js`,\n      sourcemap: true,\n      exports: \'named\',\n    },\n    plugins,\n    external: [\'react\', \'react-dom\'],\n  }\n})\n\n\nexport default [\n  {\n    input: [\'src/index.ts\'],\n    output: [\n      {\n        file: packageJson.module,\n        format: \'esm\',\n        sourcemap: true,\n        exports: \'named\',\n      },\n    ],\n    plugins,\n    external: [\'react\', \'react-dom\'],\n  },\n  ...folderBuilds,\n  {\n    input: [\'src/index.ts\'],\n    output: [\n      {\n        file: packageJson.main,\n        format: \'cjs\',\n        sourcemap: true,\n        exports: \'named\',\n      },\n    ],\n    plugins,\n    external: [\'react\', \'react-dom\'],\n  },\n];\n\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. CJS文件
  2. \n
\n
    \n
  • 最后我还添加了汇总配置来生成 cjs 文件。我没有费心对 cjs 文件进行代码分割,因为大多数用户都使用 es6 导入
  • \n
\n
    \n
  1. “坦率”构建
  2. \n
\n

构建后,我运行一个脚本将粘贴 package.json、Readme 复制到 ./dist 文件夹

\n
/* eslint-disable no-console */\nconst { resolve, join, basename } = require(\'path\');\nconst { readFile, writeFile, copy } = require(\'fs-extra\');\n\nconst packagePath = process.cwd();\nconst distPath = join(packagePath, \'./dist\');\n\nconst writeJson = (targetPath, obj) =>\n  writeFile(targetPath, JSON.stringify(obj, null, 2), \'utf8\');\n\nasync function createPackageFile() {\n  const packageData = await readFile(\n    resolve(packagePath, \'./package.json\'),\n    \'utf8\'\n  );\n  const { scripts, devDependencies, ...packageOthers } =\n    JSON.parse(packageData);\n  const newPackageData = {\n    ...packageOthers,\n    private: false,\n    typings: \'./index.d.ts\',\n    main: \'./main.js\',\n    module: \'./index.js\',\n  };\n\n  const targetPath = resolve(distPath, \'./package.json\');\n\n  await writeJson(targetPath, newPackageData);\n  console.log(`Created package.json in ${targetPath}`);\n}\n\nasync function includeFileInBuild(file) {\n  const sourcePath = resolve(packagePath, file);\n  const targetPath = resolve(distPath, basename(file));\n  await copy(sourcePath, targetPath);\n  console.log(`Copied ${sourcePath} to ${targetPath}`);\n}\n\nasync function run() {\n  try {\n    await createPackageFile();\n    await includeFileInBuild(\'./README.md\');\n    // await includeFileInBuild(\'../../LICENSE\');\n  } catch (err) {\n    console.error(err);\n    process.exit(1);\n  }\n}\n\nrun();\n\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. 终于从根源上npm publish ./dist
  2. \n
\n

这就是我的 dist 文件夹最终的样子

\n
dist\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Accordion\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Accordion.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionBody.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionButton.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionCollapse.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionContext.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionHeader.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionItem.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 AccordionItemContext.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Alert\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Alert.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Badge\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Badge.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Breadcrumb\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Breadcrumb.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 BreadcrumbItem.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Button\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Button.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ButtonGroup\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ButtonGroup.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n...\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Tooltip\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Tooltip.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 TooltipBox.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.js.map\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package.json\n
Run Code Online (Sandbox Code Playgroud)\n

经过对 gh 上的汇总问题线程进行大量研究后,我得到了解决方案。\n以下是一些参考资料:

\n
    \n
  1. 弗兰克构建:/sf/ask/4376287751/# :~:text=Votes-,13,-This%20is%20possible
  2. \n
  3. 文件夹结构:\n https://github.com/ezolenko/rollup-plugin-typescript2/issues/136#issuecomment-792383946
  4. \n
  5. 我写的 getFolders() 的灵感来自作者的 getFiles()\n https://www.codefeetime.com/post/rollup-config-for-react-component-library-with-typescript-scss/
  6. \n
\n