创建一个Typescript + React公共库

时间:2019-03-22 21:07:06

标签: node.js reactjs typescript webpack

我试图创建一个通用的React和Typescript通用库,为了完全开发库和应用程序,在配置项目工作区时遇到了太多困难

project
├─ app
│  ├─ package.json
│  ├─ tsconfig.json
│  ├─ webpack.config.js
│  ├─ ... (other config files)
│  ├─ node_modules
│  │  ├─ ...
│  │  ├─ @types
│  │  │  ├─ ...
│  │  │  └─ react
│  │  └─ symlink(lib A)
│  └─ src
│     ├─ *.ts files
│     ├─ *.tsx files
│     └─ index.ts
│
└─ libA
   ├─ package.json
   ├─ tsconfig.json
   ├─ other config files
   ├─ node_modules
   │  ├─ ...
   │  └─ @types
   │     ├─ ...
   │     └─ react
   └─ src
      ├─ *.ts files
      ├─ *.tsx files
      └─ index.ts
  • 如果 libA 中的源文件位于 app 中,则所有内容都会编译并按预期运行。

  • 在此配置下, libA 已成功构建(tsc)。

  • 在此配置下, app 不能与以下内容一起编译:

../libA/node_modules/@types/react/index.d.ts:2963:13 - error TS2717: Subsequent property declarations must have the same type.  Property 'view' must be of type 'SVGProps<SVGViewElement>', but here has type 'SVGProps<SVGViewElement>'.

一些Google搜索建议我应该从 libA 中的node_modules中删除反应类型,但这会破坏开发过程的便利性,因为 libA 无法编译。 / p>


更多详细信息:

I develop on Mac.
npm: 6.4.1
node: 8.15.0
typescript: 3.3.3333
webpack: 4

tsconfig libA:

{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "declaration": true,
    "sourceMap": true,
    "target": "es5",
    "module": "commonjs",
    "outDir": "dist",
    "moduleResolution": "node",
    "jsx": "react",
    "experimentalDecorators": true,
    "baseUrl": "src"
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

tsconfig应用程序:

{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "removeComments": true,
    "sourceMap": true,
    "target": "es5",
    "module": "commonjs",
    "outDir": "dist",
    "moduleResolution": "node",
    "jsx": "react",
    "lib": [
      "es5",
      "es6",
      "es7",
      "dom"
    ],
    "baseUrl": "src"
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

不想使用 skipLibCheck ,因为类型是我一开始就爱上打字稿的原因...

可以肯定地认为我做错了什么。.因为我要花近一个星期的时间来打开它,并且无法确定正确的配置。.欢迎任何指导...


经过进一步调查,我发现唯一有所作为的是我是否通过npm链接使用到 libA 的符号链接

3 个答案:

答案 0 :(得分:0)

最好,最简单的方法就是使用webpack或包裹

您将希望您的配置类似于以下内容。

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, "../build");
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,
};

要了解的重要事项: libraryTarget:commonjs必须与您的tsconfig中的“模块” commonjs相匹配。 awesome-typescript-loader / webpack-cli可以是运行此程序的最新版本。 您将需要tsconfig.prod.json或更改配置的一部分。

react / reactdom / styled-components被列为“外部”,这意味着它们将不会与您的应用程序捆绑在一起,webpack会在您要导入的包中查找那些模块。

Main.tsx应该只继续导出到commonjs捆绑包中所需的每个组件,它应该看起来像这样。 // main.tsx

export { Button } from "./components/Button/Button";
export { Checkbox } from "./components/Checkbox/Checkbox";
export { DataTable } from "./components/DataTable/DataTable

测试此构建的一种快速方法是删除所有“外部”构建,然后尝试require(“ .. path ... to..bundle”)。按钮并用console.log记录。

您需要index.js和index.d.ts才能发布到私有或公共npm包

编辑:您将使用类似以下内容的webpack命令。

  "build": "webpack --config ./folder/to/config//webpack.component-prod.js",

答案 1 :(得分:0)

我建议将lerna与纱线的工作区功能一起使用。

您的问题是,当您有多个软件包(取决于同一个软件包)时,它们可能引用同一个类型定义,该定义在不同的路径中存在两个副本(由于符号链接)。

请参阅:https://github.com/Microsoft/typescript/issues/6496

更多详细信息,请搜索以下关键字:“ lerna打字稿重复标识符符号链接”

接口可以合并,但类不能合并。因此,如果您从不同的包中导入模块,或者使用lerna的symlink安装依赖项,并且有两个不同的路径来查找相同的类型定义,则对类的typecheck将失败。

因此,您需要确保只有一个版本/位置的包具有类型定义。这正是yarn的工作区功能所做的-在工作区的顶层安装所有子包的依赖项。

但是请注意,由于webpack的模块解析策略,纱线的工作区功能破坏了webpack的外部选项。

答案 2 :(得分:0)

所以这是一个非常非常漫长的旅程。.但是我们已经完成了..从那时起,我们采用了最初的POC并将其转换为完整的框架。

Thunderstorm