【问题标题】:Create a Typescript + React common library创建一个 Typescript + React 通用库
【发布时间】:2019-08-13 22:08:21
【问题描述】:

我尝试创建一个通用的 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>'.

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


更多细节:

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,因为类型是我首先爱上 typescript 的原因...

可以安全地假设我做的事情完全错误..因为我断断续续地做了将近一个星期,无法确定正确的配置..欢迎任何指导...


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

【问题讨论】:

  • 这个问题已经解决了,我不知道为什么......一旦我知道我会在这里评论它

标签: node.js reactjs typescript webpack


【解决方案1】:

最好和最简单的方法就是使用 webpack 或 parcel

你会希望你的配置类似于这样..

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").Button 和 console.log。

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

编辑:您将使用该 webpack 命令与类似的东西..

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

【讨论】:

  • 谢谢,但这不是我想要的方向。我希望 tsc 能够编译项目而不管 webpack 没有任何问题
【解决方案2】:

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

您的问题是,当您有多个依赖于同一个包的包时,它们可能会引用在不同路径中存在两个副本的相同类型定义(因为符号链接)。

见:https://github.com/Microsoft/typescript/issues/6496

更多细节,搜索这些关键字:“lerna typescript duplicate identifier symlink”

接口可以合并,但类不能。所以如果你从不同的包中导入模块,或者使用lerna的符号链接安装依赖,并且有两个不同的路径找到相同的类型定义,类的类型检查将失败。

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

但请注意,由于 webpack 的模块解析策略,yarn 的工作区功能会破坏 webpack 的 externals 选项。

【讨论】:

    【解决方案3】:

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

    Thunderstorm

    【讨论】:

      猜你喜欢
      • 2020-10-21
      • 1970-01-01
      • 2019-06-18
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      • 2023-02-08
      • 2023-03-27
      • 2020-01-14
      相关资源
      最近更新 更多