创建一个React组件库

时间:2019-03-05 05:44:45

标签: reactjs typescript webpack babeljs

我正在使用React和带有Babel 7的TypeScript创建一个模块化组件库。

我希望我的图书馆用户以类似于以下的语法导入组件:

import SomeComponent from "my-awesome-lib/SomeComponent"

SomeComponent是my-awesome-lib软件包中的TSX模块:

import * as React from "react";

export default function () {
  return <h1>SomeComponent</h1>
}

my-awesome-component的package.json文件的main字段是:

"main": "src/index.ts"

我不想发布组件库的编译版本,因为我正在组件中导入CSS和其他资产,并且我希望我的软件包的所有用户都可以通过某些特定的配置使用Webpack。

现在我的问题是`从“ my-awesome-lib / SomeComponent”导入SomeComponent失败,并出现解析错误:

ERROR in ../node_modules/wtf/index.tsx 4:9
Module parse failed: Unexpected token (4:9)
You may need an appropriate loader to handle this file type.
| 
| export default function () {
>   return <h1>WTF</h1>
| }

似乎Webpack不会在node_modules中加载或转换TSX文件。

我在用户应用程序的根目录(导入my-awesome-lib)中使用了tsconifg.json:

{
  "compilerOptions": {
    "outDir": "./dist",
    "module": "commonjs",
    "target": "es5",
    "jsx": "react",
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "downlevelIteration": true,
    "lib": ["es5", "es2015", "dom", "scripthost"], 
    "typeRoots": ["node_modules/@types", "src/@types"],
  },
  "include": ["src/**/*", "node_modules/**/*"],
  "exclude": []
}

Webpack的相关配置为:

const tsModules = {
  test: /\.(js|jsx|ts|tsx)$/,
  include: [path.resolve('src')],
  exclude: /node_modules/,
  loader: 'babel-loader'
}

const resolve = {
  alias: {
  },
  modules: [
    'node_modules'
  ],
  extensions: ['.tsx', '.ts', '.js']
}

module.exports = {
   ...
   context: resolve('src'),
   resolve: resolve,
   module: {
     rules: [
       tsModules,
       ...
     ]
   }
}

如何使Webpack从node_modules加载和转换my-awesome-lib的TSX模块?

3 个答案:

答案 0 :(得分:1)

您正在排除node_modules目录(通常是好事):

const tsModules = {
  test: /\.(js|jsx|ts|tsx)$/,
  include: [path.resolve('src')],
  exclude: /node_modules/,
  loader: 'babel-loader'
}

除了明确排除node_modules文件夹之外,您还只允许src处理babel-loader文件夹的内容,因为您在{{1 }}。所以这个错误:

  ../node_modules/wtf/index.tsx中的

ERROR 4:9模块解析失败:   意外令牌(4:9)

很有道理。

如果您删除了include属性并更改了tsModules中的正则表达式,则仍然可以排除node_modules到单个文件夹之外的内容:

tsModules.include

或者可以,但是我还没有测试过,请将tsModules.exclude目录添加到const tsModules = { // ... exclude: /node_modules\/(?!my-awesome-lib)\/*/ // ... } 数组中:

my-awesome-lib

然后include目录中的文件将通过const tsModules = { // ... include: [ path.resolve(__dirname, './src'), path.resolve(__dirname, './node-modules/my-awesome-lib') ] // ... } ,它将转换打字稿代码。

编辑:我认为您的node_modules/my-awesome-lib文件与babel-loader引起了混淆。 Babel正在编译您的代码,而不是打字稿。因此,在根目录中有一个tsconfig.json文件可能会帮助您的IDE(特别是如果您使用的是Microsoft的VScode),但对"include": ["src/**/*", "node_modules/**/*"],tsconfig.json的代码转换方式没有影响。

答案 1 :(得分:0)

此设置假定您使用的是样式组件,并且没有css / scss。

这里重要的是,您在tsconfig中具有“模块”:commonJS,而在webpack配置中具有libraryTarget:“ commonJS”。外部人员告诉webpack不要将您的库与React,React-DOM或样式化组件捆绑在一起,而是在要导入的项目内查找那些包。

您还需要从package.json依赖项中删除React,react-dom和styled-components并将这些包置于对等项依赖项中

   const path = require("path");
    const fs = require("fs");
    const TerserPlugin = require('terser-webpack-plugin');
    const appIndex = path.join(__dirname, "../src/main.tsx");
    const appBuild = path.join(__dirname, "../storybook-static");
    const { TsConfigPathsPlugin } = require('awesome-typescript-loader');

    module.exports = {
        context: fs.realpathSync(process.cwd()),
        mode: "production",
        bail: true,
        devtool: false,
        entry: appIndex,
        output: {
            path: appBuild,
            filename: "dist/Components.bundle.js",
            publicPath: "/",
            libraryTarget: "commonjs"
        },
        externals: {
            react: {
                root: 'React',
                commonjs2: 'react',
                commonjs: 'react',
                amd: 'react'
            },
            'react-dom': {
                root: 'ReactDOM',
                commonjs2: 'react-dom',
                commonjs: 'react-dom',
                amd: 'react-dom'
            },
            "styled-components": {
                root: "styled-components",
                commonjs2: "styled-components",
                commonjs: "styled-components",
                amd: "styled-components"
            }
        },
        optimization: {
            minimizer: [
                new TerserPlugin({
                    terserOptions: {
                        parse: {
                            ecma: 8,
                        },
                        compress: {
                            ecma: 5,
                            warnings: false,
                            comparisons: false,
                            inline: 2,
                        },
                        mangle: {
                            safari10: true,
                        },
                        output: {
                            ecma: 5,
                            comments: false,
                            ascii_only: true,
                        },
                    },
                    parallel: true,
                    cache: true,
                    sourceMap: false,
                })
            ],
        },
        resolve: {
            extensions: [".web.js", ".mjs", ".js", ".json", ".web.jsx", ".jsx", ".ts", ".tsx"],
            alias: {
                "react-native": "react-native-web",
            },
        },
        module: {
            strictExportPresence: true,
            rules: [
                { parser: { requireEnsure: false } },
                {
                    test: /\.(ts|tsx)$/,
                    loader: require.resolve("tslint-loader"),
                    enforce: "pre",
                },
                {
                    oneOf: [
                        {
                            test: /\.(tsx?)$/,
                            loader: require.resolve('awesome-typescript-loader'),
                            options: {
                                configFileName: 'tsconfig.prod.json'
                            }
                        },
                    ],
                },
            ],
        },
        plugins: [
            new TsConfigPathsPlugin()
        ],
        node: {
            dgram: "empty",
            fs: "empty",
            net: "empty",
            tls: "empty",
            child_process: "empty",
        },
        performance: false,
    };

注意:将应用程序中的一个点定位为仅包含要导出的组件的条目非常重要。

IE对我来说是Main.tsx,而在Main.tsx内看起来像这样。

export { Checkbox } from "./components/Checkbox/Checkbox";
export { ColorUtils } from "./utils/color/color";
export { DataTable } from "./components/DataTable/DataTable";
export { DatePicker } from "./components/DateTimePicker/DatePicker/DatePicker";
export { DateTimePicker } from "./components/DateTimePicker/DateTimePicker/DateTimePicker";
export { Disclosure } from "./components/Disclosure/Disclosure";

这意味着webpack不会捆绑您无意导出的内容。要测试您是否捆绑了作品,请尝试从捆绑中导入具有require语法的内容,以避开打字稿类型,并在tsconfig中将allowJS设置为true。

类似 const Button = require(“ ../ path / to / js / bundle”)。Button console.log(Button);

答案 2 :(得分:0)

我发现create-react-library很有帮助